导读:本期聚焦于弦宿​创作的《php怎么实现用户行为聚类分析?用无监督学习划分用户群实操详解》,敬请观看详情。用户量上来之后,运营同学最常问的问题就是:我们的用户到底能分成几类?哪些是高价值用户,哪些在流失边缘?这篇文章用PHP从零实现一套用户行为聚类方案,核心思路是先从日志中提取访问频次、停留时长、转化次数等行为特征并做标准化,再借助K-Means算法把特征相近的用户自动归到同一簇,最后通过轮廓系数确定最佳聚类数。文中还提供了纯PHP实现与php-ml库两种方案的代码对比,讲解了K值选择、特征权重、离群点处理等实战细节,并给出聚类结果在精准推送和流失预警中的落地用法,适合想在已有PHP系统里直接跑通聚类分析的读者参考。

做用户运营最头疼的事情之一,就是面对几十万用户却不知道他们之间有什么差异。比如同样是一天访问三次,有人每次停留几秒就走,有人能看十几分钟商品详情;有人从不下单,有人每月固定复购。如果只看平均值,这些差异会被完全抹平。聚类分析恰好是解决这个问题的利器——它不需要提前给用户打标签,而是让算法根据行为特征自动把相似的用户划到一起。本文用PHP来实现这套流程,从数据准备、特征提取到K-Means聚类,完整走一遍。

php怎么实现用户行为聚类分析?用无监督学习划分用户群实操详解

一、聚类之前:先把用户行为变成可计算的向量

聚类算法不能直接吃原始日志,它只认数字。所以第一步是把每个用户的行为汇总成一组的特征值,这一步做的好坏直接决定聚类效果。常见的用户行为特征包括:近30天的访问次数、平均单次停留时长、下单次数、浏览品类数量、活动参与次数等等。

假设我们有一张用户行为日志表,结构大致如下:

CREATE TABLE user_behavior_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    action VARCHAR(50) NOT NULL,  -- pv, order, cart, share 等
    duration INT DEFAULT 0,       -- 本次行为停留秒数
    created_at DATETIME NOT NULL,
    INDEX idx_user (user_id, created_at)
);

接着写一个SQL把这些离散的日志聚合成每个用户一行特征:

SELECT
    user_id,
    COUNT(*) AS total_actions,
    SUM(CASE WHEN action = 'order' THEN 1 ELSE 0 END) AS order_count,
    SUM(CASE WHEN action = 'cart' THEN 1 ELSE 0 END) AS cart_count,
    ROUND(AVG(duration), 1) AS avg_duration,
    COUNT(DISTINCT CASE WHEN action = 'pv' THEN created_at DIV 10000 ELSE NULL END) AS active_slots
FROM user_behavior_log
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY user_id;

拿到特征之后有个非常关键的步骤——标准化。访问次数可能是几百,而下单次数可能只有个位数,量纲差距太大时,距离计算会被大数值特征主导,聚类结果会严重失真。最常用的是Z-Score标准化,把每一列转成均值为0、标准差为1的分布:

<?php
function standardize(array $matrix): array
{
    $colCount = count($matrix[0]);
    $rowCount = count($matrix);
    $result = [];

    for ($c = 0; $c < $colCount; $c++) {
        // 计算每一列的均值
        $sum = 0;
        for ($r = 0; $r < $rowCount; $r++) {
            $sum += $matrix[$r][$c];
        }
        $mean = $sum / $rowCount;

        // 计算标准差
        $variance = 0;
        for ($r = 0; $r < $rowCount; $r++) {
            $variance += pow($matrix[$r][$c] - $mean, 2);
        }
        $std = sqrt($variance / $rowCount) ?: 1;

        for ($r = 0; $r < $rowCount; $r++) {
            $result[$r][$c] = ($matrix[$r][$c] - $mean) / $std;
        }
    }
    return $result;
}

这里有个细节值得注意:如果某列标准差为0(比如所有用户都没有分享行为),直接除零会报错,所以代码里用了?: 1兜底。另外对于严重偏斜的指标,比如下单次数,建议先做对数变换log(1+x)再标准化,否则头部少量剁手党会把整个分布拉歪。

二、用纯PHP手写K-Means聚类

K-Means是无监督学习里最经典也最容易落地的聚类算法,原理并不复杂:先随机挑K个点当簇中心,然后把每个样本分配给最近的中心,再根据分配结果重新计算各簇的中心点,反复迭代直到中心不再明显移动。下面是完整的PHP实现:

<?php
class KMeans
{
    private int $k;
    private int $maxIterations;

    public function __construct(int $k = 4, int $maxIterations = 100)
    {
        $this->k = $k;
        $this->maxIterations = $maxIterations;
    }

    // 计算两个向量之间的欧氏距离
    private function distance(array $a, array $b): float
    {
        $sum = 0;
        for ($i = 0; $i < count($a); $i++) {
            $sum += pow($a[$i] - $b[$i], 2);
        }
        return sqrt($sum);
    }

    public function cluster(array $data): array
    {
        $n = count($data);
        // 随机选取K个样本作为初始中心
        shuffle($data);
        $centroids = array_slice($data, 0, $this->k);
        $assignments = array_fill(0, $n, -1);

        for ($iter = 0; $iter < $this->maxIterations; $iter++) {
            $changed = false;

            // 第一步:把每个点分配给最近的中心
            foreach ($data as $i => $point) {
                $bestCluster = 0;
                $bestDist = PHP_FLOAT_MAX;
                foreach ($centroids as $c => $centroid) {
                    $d = $this->distance($point, $centroid);
                    if ($d < $bestDist) {
                        $bestDist = $d;
                        $bestCluster = $c;
                    }
                }
                if ($assignments[$i] !== $bestCluster) {
                    $assignments[$i] = $bestCluster;
                    $changed = true;
                }
            }

            if (!$changed) break; // 已经收敛,提前退出

            // 第二步:重新计算每个簇的中心
            for ($c = 0; $c < $this->k; $c++) {
                $sums = array_fill(0, count($data[0]), 0);
                $count = 0;
                foreach ($data as $i => $point) {
                    if ($assignments[$i] === $c) {
                        for ($j = 0; $j < count($point); $j++) {
                            $sums[$j] += $point[$j];
                        }
                        $count++;
                    }
                }
                if ($count > 0) {
                    for ($j = 0; $j < count($sums); $j++) {
                        $centroids[$c][$j] = $sums[$j] / $count;
                    }
                }
            }
        }

        return $assignments;
    }
}

使用方式很简单,传入标准化后的特征矩阵即可:

<?php
// $rows 是从数据库取出的特征数组
$data = standardize($rows);
$kmeans = new KMeans(4);
$labels = $kmeans->cluster($data);

// 把聚类结果写回用户表
foreach ($rows as $i => $row) {
    $stmt = $pdo->prepare("UPDATE users SET cluster_id = ? WHERE id = ?");
    $stmt->execute([$labels[$i], $row['user_id']]);
}

需要提醒的是,K-Means对初始中心敏感,随机初始化可能陷入局部最优。改进办法是多次运行取效果最好的一次,或者采用K-Means++初始化策略——让初始中心尽量彼此远离。对于几万条数据、五六个特征的场景,纯PHP跑一次也就几秒钟,完全够用;但如果用户量到了百万级,建议把计算部分改成离线脚本定时跑,或者直接交给数据库端的聚合来分担压力。

三、K值怎么选?用轮廓系数自动评估

聚成几类合适是新手最常踩的坑。K太小则用户被粗暴合并,K太大则每类人太少没法运营。硬拍脑袋不如用指标说话,轮廓系数(Silhouette Coefficient)是评估聚类质量最直观的指标,取值范围在-1到1之间,越接近1说明该点离自己的簇越近、离别的簇越远,整体平均轮廓系数最高的K通常就是较优选择。

计算逻辑为:对每个样本,算它同簇其他点的平均距离a(凝聚度),再算它到最近的其他簇各点的平均距离b(分离度),轮廓值就是(b - a) / max(a, b)。完整实现:

<?php
function silhouetteScore(array $data, array $labels): float
{
    $n = count($data);
    $distances = [];
    // 预计算两两距离,样本多时可改用分块计算
    for ($i = 0; $i < $n; $i++) {
        for ($j = $i + 1; $j < $n; $j++) {
            $d = 0;
            for ($t = 0; $t < count($data[$i]); $t++) {
                $d += pow($data[$i][$t] - $data[$j][$t], 2);
            }
            $distances[$i][$j] = $distances[$j][$i] = sqrt($d);
        }
    }

    $total = 0;
    for ($i = 0; $i < $n; $i++) {
        $inCluster = [];  // 同簇内距离
        $outClusters = []; // 各外簇距离
        for ($j = 0; $j < $n; $j++) {
            if ($i === $j) continue;
            if ($labels[$j] === $labels[$i]) {
                $inCluster[] = $distances[$i][$j];
            } else {
                $outClusters[$labels[$j]][] = $distances[$i][$j];
            }
        }
        $a = $inCluster ? array_sum($inCluster) / count($inCluster) : 0;
        $b = PHP_FLOAT_MAX;
        foreach ($outClusters as $ds) {
            $b = min($b, array_sum($ds) / count($ds));
        }
        if ($b === PHP_FLOAT_MAX) continue;
        $total += ($b - $a) / max($a, $b);
    }
    return $total / $n;
}

// 遍历不同K值,找出轮廓系数最高的那个
for ($k = 2; $k <= 8; $k++) {
    $kmeans = new KMeans($k);
    $labels = $kmeans->cluster($data);
    printf("K=%d, 轮廓系数=%.3f\n", $k, silhouetteScore($data, $labels));
}

注意两两距离的计算复杂度是O(n²),用户量大时全量计算不现实,可以随机抽样一两千个点来估算轮廓系数,效果足够指导K的选择。另外肘部法则也是常见手段:画出不同K对应的簇内误差平方和曲线,找拐点位置。实际操作中建议两种方法结合,再叠加业务可解释性做最终决定——毕竟聚出来的每个簇都需要能被运营同学理解和运用。

四、不想手写?直接用php-ml库搞定

如果不想维护自己的算法实现,PHP生态里有个现成的机器学习库php-ml,通过Composer安装即可:

composer require php-ai/php-ml

用php-ml做K-Means聚类只需要十几行代码:

<?php
require 'vendor/autoload.php';

use Phpml\Clustering\KMeans;
use Phpml\Preprocessing\Normalizer;

// $samples 是特征数组,每个元素为一行数值
$normalizer = new Normalizer(Normalizer::NORM_STD);
$normalizer->transform($samples);

$kmeans = new KMeans(4);
$clusters = $kmeans->cluster($samples);

foreach ($clusters as $clusterId => $points) {
    echo "簇 {$clusterId} 包含 " . count($points) . " 个用户\n";
}

php-ml内置了标准化器,也自带K-Means++初始化,省去了不少细节工作。除此之外它还提供DBSCAN等基于密度的聚类算法,DBSCAN的优势是不需要预先指定K,而且能自动识别离群点——那些行为极度异常的用户(比如爬虫账号)会被标记为噪声而不强行归入任何簇,这在真实数据里非常有价值。缺点是它对参数(邻域半径和最小点数)比较敏感,需要结合标准化后的数据分布反复调试。

五、聚类结果怎么落地到业务

算法跑完只是开始,真正的价值在于结果解读和运营应用。拿到每个簇之后,应该统计各簇的特征均值,转成运营能看懂的语言,比如:簇1平均月访问20次、下单4次、停留8分钟,这就是核心活跃买家;簇2访问频繁但零下单,是典型的只逛不买型,适合发优惠券刺激首单;簇3各项指标都垫底且呈下降趋势,是流失预警对象,需要召回策略介入。

<?php
// 输出每个簇的特征画像
$sql = "SELECT cluster_id,
               COUNT(*) AS user_cnt,
               ROUND(AVG(total_actions),1) AS avg_actions,
               ROUND(AVG(order_count),2) AS avg_orders,
               ROUND(AVG(avg_duration),1) AS avg_stay
        FROM users JOIN user_features USING(user_id)
        GROUP BY cluster_id";
foreach ($pdo->query($sql) as $row) {
    printf("簇%s: %d人, 日均行为%s次, 下单%s单, 停留%s秒\n",
        $row['cluster_id'], $row['user_cnt'],
        $row['avg_actions'], $row['avg_orders'], $row['avg_stay']);
}

落地时还有三点经验值得分享。第一,聚类要定期重跑,用户行为会随季节、活动、产品改版漂移,上个月的分群这个月可能就不准了,建议按周或按月更新。第二,聚类结果要和业务指标做交叉验证,比如统计各簇的GMV贡献和次月留存,确认分群确实区分出了价值差异,而不是算法层面的自说自话。第三,特征工程比算法选择更重要,与其纠结用K-Means还是DBSCAN,不如多花时间设计更贴近业务的行为指标,比如加入时间衰减因子,让近期的行为权重更高,往往能带来立竿见影的聚类质量提升。

整套方案的核心链路可以总结为:日志聚合、特征标准化、确定K值、聚类计算、结果回写、业务解读。PHP虽然不是机器学习的主流语言,但在数据规模可控、团队技术栈以PHP为主的前提下,这套方案的开发和维护成本远低于引入Python服务,对于绝大多数中小型网站的用户分群需求完全够用。

php聚类分析用户行为分析无监督学习修改时间:2026-09-10 01:01:01

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0910/53708.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。