运用kNN算法识别潜在续费商家

背景与目标

Youzan 是一家SAAS公司,服务于数百万商家,帮助互联网时代的生意人私有化顾客资产、拓展互联网客群、提高经营效率。现在,该公司希望能够从商家的交易数据中,挖掘出有强烈续费倾向的商家,并提供更优质更有针对性的服务。

目标: 从商家交易数据中识别有强烈续费倾向的商家。

思路与建模

kNN是一种思路简单清晰的有点近似蛮力的机器学习算法。它将待分类数据的特征值集与已分类数据集的每个样本的特征值集进行比较,计算出距离值,然后根据距离最小原则,选择k个距离最小的已分类实例,从这k个已分类实例中选择分类概率最大(出现次数最多)的那个。

从商家交易数据中识别有强烈续费倾向的商家,首先进行建模:

商家的强烈续费意向与哪些交易数据密切关联呢?这里,作出如下假设: 商家的强烈续费意向与“日均成交订单数”、“日均实际成交金额”、“日均累计粉丝数” 三个特征值关联。 下面,就说明运用kNN算法识别潜在续费商家的步骤。 完整源代码见附录。

本文的源代码来自《机器学习实战》 第二章。实现用到了矩阵计算,在代码讲解中会细说。

步骤与实现

训练样本集

首先需要准备一个训练样本集,里面含有每个商家的“日均成交订单数”、“日均实际成交金额”、“日均累计粉丝数”、“是否已续费”。为方便起见,可以准备一个原始的数据文件 origin.txt ,然后通过随机值增加的方式(见源代码中的 createMoreSamplesFrom(filename, n)) 生成更大的实际样本集 sample.txt。 这里数据都是伪造的。

12 789 11 0
1089 1200000 134 1
10 9800000 789 1
244 98700 256 0
1234 987653 900 0
40000 50000000 14000 1
30000 600000 120000 1
2500 4700000 9000 1
3 278 55 0
5890 2457788 130000 1
702 89032 890 0
12456 87200012 75000 1
125 90321133 45001 1
600 300020 120000 1
1456 90224441 900000 1
456 12456 2356 0
8 1204 236 0
129000 135700000 8000000 1

数据归一化

可以看到,日均实际成交金额的值非常大,因此特征值“日均实际成交金额”的距离会起决定性的作用,弱化其他特征值的影响。可以采用数据归一化,使得特征值的绝对值影响弱化。 见函数 autoNorm(dataset)。

对于一个序列 x = [x1, x2, ..., xN] 来说, 归一化数值 xi = (xi - min(x)) / (max(x) - min(x)) 。

矩阵归一化的计算逻辑如下: 假设 x = [[16,4,9], [3,15,27]] ,

step1: 计算每列的最小值 min(x) = [3,4,9] 以及最大值 max(x) = [16,15,27]

step2: 计算最大值与最小值之间的距离:ranges = max(x) - min(x) = [13,11,18]

step3: 计算每行的归一化数值为 ([16,4,9] - [3,4,9]) / [13,11,18] = [1,0,0] , ( [3, 15, 27] - [3,4,9] ) / [13,11,18] = [0, 1, 1] ;这里都是逐个值对应计算。

step4: 得到原矩阵 x 的归一化数值为 [[1,0,0], [0,1,1]]

def autoNorm(dataset):
    minVals = dataset.min(0)
    maxVals = dataset.max(0)
    ranges = maxVals - minVals
    normDataset = zeros(shape(dataset))
    rows = dataset.shape[0]
    normDataset = dataset - tile(minVals, (rows,1))
    normDataset = normDataset / tile(ranges, (rows,1))
    return normDataset, ranges, minVals

可视化数据

可以通过图形初步展示数据的形状, 见函数 draw(dataset)

计算距离向量

得到归一化数据后,可以计算输入向量与每一个样本实例的距离,得到距离向量。见函数 computeDistance(inX, dataset) 。 这里 inX 是输入向量,dataset 是训练样本矩阵。

这里为了计算 inX 与每一个样本实例的距离值,需要将 inX 在行方向上平铺N次,N 是 dataset 的总行数,因此使用 tile(inX, (dataset.shape[0],1)); 然后进行矩阵减法和平方,再将每行的平方和进行求根,得到输入向量与每个样本实例的距离。

比如 inX = [0.1,0.5,0.8] , dataset = [[1,0,0], [0,1,1]] , 距离向量为 D; D.rows(1) = ((-0.9)^2 + (0.5)^2 + (0.8) ^2)^(0.5) = 1.3038 ; D.rows(2) = (0.01 + 0.25 + 0.04)^(0.5) = 0.5477 ; D = [1.3038, 0.5477]

def computeDistance(inX, dataset):
    datasetrows = dataset.shape[0]
    diffMat = tile(inX, (datasetrows, 1)) - dataset
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    return distances

分类算法

得到距离向量后,就可以进行分类了。先对距离向量从小到大进行排序,然后取前k个样本实例的分类值 class[k],接着计算这k个分类值中哪个分类值的出现概率最大。 见 函数 classify(inX, dataset, labels, k) , 这里 labels 是样本集的分类标签向量,k 是算法的设置变量,取前k个距离最近的样本的分类标签。

    distances = computeDistance(inX, dataset)
    sortedDistIndicies = distances.argsort()
    classCount = {}
    for i in range(k):
        voteLabel = labels[sortedDistIndicies[i]]
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

分类算法的错误率

评价分类算法的错误率是非常重要的。见函数 computeErrorRatio。

将已含分类标签值的样本集实例TotalSample分为两类,一个是用于计算距离向量的样本集 Sample,一个用于测试分类错误率的输入向量集合 inMatrix。一般 inMatrix 占总TotalSample的 10%,可以作为算法设置 ratio 。 对于 inMatrix 的每个输入向量,得到其分类标签值,然后与其已有的分类值比较,得到正确或错误的分类结果。分类算法的错误率 = 分类错误的结果数 / inMatrix 的总数。

对未知数据分类

假设这个分类算法的错误率在接受范围内,那么就可以使用这个分类算法对未知数据分类了。注意,未知数据也要进行归一化。 见函数 classifyInstance(dataMatrix,classLabelVector, ranges, minVals)。

从其他数据文件读取待分类的数据,每行一个, 转换成输入向量 inX ,然后使用 classify 函数进行分类即可得到其分类值。

至此,kNN 算法的基本步骤和实现就完成了。

完整源代码

# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# Name:          potiential_users.py
# Purpose:       recognise potiential renewal users using kNN algorithm
#
# Author:       qin.shuq
#
# Created:      2018/03/10
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import random
import matplotlib
import matplotlib.pyplot as plot
from numpy import *
import operator

indicatorNumber = 3
k = 5

def createMoreSamplesFrom(filename, n):
    datar = open(filename)
    lines = datar.readlines()
    datar.close()

    lineNum = len(lines)
    totalLines = lineNum * n
    dataw = open('sample.txt', 'w')
    for i in range(totalLines):
        data = map (lambda x: int(x), lines[random.randint(0,lineNum)].strip().split())
        (orderNumber, actualDeal, fans, classifierResult) = (data[0] + random.randint(0, 500), data[1] + random.randint(0, 500000), data[2] + random.randint(0, 100000), data[3])
        dataw.write('%d %d %d %d\n' % (orderNumber, actualDeal, fans, classifierResult))
    dataw.close()

def file2matrix(filename):
    dataf = open(filename)
    lines = dataf.readlines()
    dataf.close()

    numOfLines = len(lines)
    dataMatrix = zeros((numOfLines, indicatorNumber))
    classLabelVector = []
    index = 0
    for line in lines:
        line = line.strip()
        listFromLine = line.split()
        dataMatrix[index, :] = listFromLine[0:indicatorNumber]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return (dataMatrix, classLabelVector)

def autoNorm(dataset):
    minVals = dataset.min(0)
    maxVals = dataset.max(0)
    ranges = maxVals - minVals
    normDataset = zeros(shape(dataset))
    rows = dataset.shape[0]
    normDataset = dataset - tile(minVals, (rows,1))
    normDataset = normDataset / tile(ranges, (rows,1))
    return normDataset, ranges, minVals

def computeDistance(inX, dataset):
    datasetrows = dataset.shape[0]
    diffMat = tile(inX, (datasetrows, 1)) - dataset
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    return distances

def classify(inX, dataset, labels, k):
    distances = computeDistance(inX, dataset)
    sortedDistIndicies = distances.argsort()
    classCount = {}
    for i in range(k):
        voteLabel = labels[sortedDistIndicies[i]]
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

def draw(data):
    fig = plot.figure()
    ax = fig.add_subplot(111)
    ax.scatter(data[:, 0], data[:,1])
    #ax.scatter(data[:, 0], data[:,1], 20.0*array(classLabelVector), 20.0*array(classLabelVector))
    plot.title('potiential renewal users figure')
    plot.xlabel('daily order number')
    plot.ylabel('daily actual deal')
    #plot.show()

def computeErrorRatio(dataMatrix, classLabelVector):
    testRatio = 0.10
    totalRows = dataMatrix.shape[0]
    testRows = int(totalRows*testRatio)
    errorCount = 0
    for i in range(testRows):
        classifierResult = classify(dataMatrix[i,:], dataMatrix[testRows:totalRows, :], classLabelVector[testRows:totalRows], k)
        print 'classify result: %d, the real result is %d' % (classifierResult, classLabelVector[i])
        if classifierResult != classLabelVector[i]:
            errorCount += 1.0
    print 'total error rate is %f' % (errorCount / float(testRows))

def classifyInstance(dataMatrix,classLabelVector, ranges, minVals):
    dataf = open('data.txt')
    for line in dataf:
        line = line.strip()
        (kid, orderNumber, actualDeal, fans) = map(lambda x: int(x), line.split())
        input = array([orderNumber, actualDeal, fans])
        classifierResult = classify((input-minVals)/ranges, dataMatrix, classLabelVector, k)
        print '%d [orderNumber=%d actualDeal=%d fans=%d] is %s potiential renewal user' % (kid, orderNumber, actualDeal, fans, "not" if classifierResult != 1 else "" )

def test():
    x = array([[16,4,9], [3,15,27]])
    xNorm = autoNorm(x)
    print 'x: ', x , 'norm(x): ', xNorm

    inX = array([0.1,0.5,0.8])
    print 'distances: ', computeDistance(inX, xNorm[0])

if __name__ == '__main__':

    test()

    createMoreSamplesFrom('origin.txt', 10)
    (dataMatrix,classLabelVector) = file2matrix('sample.txt')
    (dataMatrix,ranges, minVals) = autoNorm(dataMatrix)

    print 'dataset: ', (dataMatrix,classLabelVector)
    draw(dataMatrix)

    computeErrorRatio(dataMatrix, classLabelVector)
    classifyInstance(dataMatrix,classLabelVector, ranges, minVals)

矩阵函数

  • array([[...]]): 将Python多维数组转化为相应的N维矩阵从而可以进行计算;
  • shape: 得到该矩阵的维度属性,是一个元组; m.shape[0],行数 ; m.shape[1] , 列数 ;
  • min(n): 得到该矩阵指定维度的最小值: m.min(0),每列的最小值 ; m.min(1), 每行的最小值;n 不大于 shape() 的长度;
  • max(n): 得到该矩阵指定维度的最大值: m.max(0), 每列的最大值;m.max(0),每行的最大值 n 不大于 shape() 的长度;
  • tile: 矩阵平铺,从指定的行方向/列方向将原矩阵进行平铺得到新的矩阵;
  • argsort: 将向量从小到大排序,排序后的每个元素在原向量中的索引值形成一个新的索引向量。
>>> import numpy as np
>>> x = np.array([[16,4,9], [3,15,27]])
>>> x
array([[16,  4,  9],
       [ 3, 15, 27]])
>>> x.shape   # 得到 x 的维度信息
(2, 3)
>>> x.shape[0]  # 总行数
2
>>> x.shape[1]  # 总列数
3
>>> x.min(0)    # 列方向的最小值
array([3, 4, 9])
>>> x.min(1)    # 行方向的最小值
array([4, 3])
>>> x.max(0)  # 列方向的最大值
array([16, 15, 27])
>>> x.max(1)  # 行方向的最大值
array([16, 27])
>>>
>>>
>>> np.tile(x, (2,1))  # 将 x 在列方向平铺2次
array([[16,  4,  9],
       [ 3, 15, 27],
       [16,  4,  9],
       [ 3, 15, 27]])

>>> np.tile(x,(1,2))   # 将 x 在行方向平铺2次
array([[16,  4,  9, 16,  4,  9],
       [ 3, 15, 27,  3, 15, 27]])

>>>
>>> np.tile(x, (2,2))    # 将 x 在行和列方向分别平铺2次
array([[16,  4,  9, 16,  4,  9],
       [ 3, 15, 27,  3, 15, 27],
       [16,  4,  9, 16,  4,  9],
       [ 3, 15, 27,  3, 15, 27]])

>>> x=np.array([1,4,3,-1,6,9])
>>> x.argsort()
array([3, 0, 2, 1, 4, 5])  # -1 是最小值,在原向量中 index = 3

优化方向

可以从“可扩展性”和“性能”来优化kNN算法的代码实现。

  • 可扩展性:在模型中增加更多的因素(比如下单/付款转化率,服务满意度等),可以不改变代码而依然执行。
  • 性能: 通过并发或分布式算法,能够在多台机器上运行 kNN 算法,实现对大规模数据样本集的kNN计算。

小结

本文讲解了如何使用kNN算法来实现识别潜在续费商家。kNN算法依赖于模型的正确性。如果分类标签值与样本特征值有非常密切的关联,使用简单的kNN算法即可得到有效的结果,而且不限于特定的应用领域。只要能够将领域问题转化为样本特征值矩阵,就能使用 kNN 算法来进行求解。

这也是我学习的第一个机器学习算法 :)

原文地址:https://www.cnblogs.com/lovesqcc/p/8543479.html

时间: 2024-08-27 12:45:41

运用kNN算法识别潜在续费商家的相关文章

基于python Knn 算法识别手写数字,计算准确率 ——第二弹

大家好~ 可爱的我又来了~ 今天我会分享一个简单的Knn算法实例,是小白的必备实例! 开始喽~ 首先我是用Jupyter新建的一个python文件 并且在相同文件夹中放了一个‘data’的图片文件夹里面从0-9个有5000张图片. 接下来是代码部分: 这是需要引用的部分数据包 建立X,y两个集合 digit =  cv2.imread('./data/%d/%d_%d.bmp'%(i,i,j)) #读取图片的位置 下面代码是转换数据类型 下面是输出结果 就这些啦!虽然还点粗糙但是我会努力哒~ 原

Python 手写数字识别-knn算法应用

在上一篇博文中,我们对KNN算法思想及流程有了初步的了解,KNN是采用测量不同特征值之间的距离方法进行分类,也就是说对于每个样本数据,需要和训练集中的所有数据进行欧氏距离计算.这里简述KNN算法的特点: 优点:精度高,对异常值不敏感,无数据输入假定 缺点:计算复杂度高,空间复杂度高 适用数据范围:数值型和标称型(具有有穷多个不同值,值之间无序)    knn算法代码: #-*- coding: utf-8 -*- from numpy import * import operatorimport

机器学习--kNN算法案例

一.改进约会网站的配对效果 想要预测的目标变量:不喜欢的人.魅力一般的人.极具魅力的人 样本特征:每年获得的飞行常客里程数.玩视频游戏所耗时间百分比.每周消费的冰淇淋公升数 约会数据信息存放在文本文件datingTestSet.txt中,每个样本数据占据一行,共有1000行. 实现步骤 1.将文本文件中的数据进行解析 ################################### #功能:将文本文件中的数据解析为矩阵形式 #输入变量:filename 文件名字符串 #输出变量:retur

Python 基于KNN算法的手写识别系统

本文主要利用k-近邻分类器实现手写识别系统,训练数据集大约2000个样本,每个数字大约有200个样本,每个样本保存在一个txt文件中,手写体图像本身是32X32的二值图像,如下图所示: 手写数字识别系统的测试代码: from numpy import * import operator from os import listdir #inX    要检测的数据 #dataSet   数据集 #labels    结果集 #k      要对比的长度 def classify0(inX, data

kNN算法python实现和简单数字识别

kNN算法 算法优缺点: 优点:精度高.对异常值不敏感.无输入数据假定 缺点:时间复杂度和空间复杂度都很高 适用数据范围:数值型和标称型 算法的思路: KNN算法(全称K最近邻算法),算法的思想很简单,简单的说就是物以类聚,也就是说我们从一堆已知的训练集中找出k个与目标最靠近的,然后看他们中最多的分类是哪个,就以这个为依据分类. 函数解析: 库函数 tile() 如tile(A,n)就是将A重复n次 a = np.array([0, 1, 2]) np.tile(a, 2) array([0,

【机器学习算法实现】kNN算法__手写识别——基于Python和NumPy函数库

[机器学习算法实现]系列文章将记录个人阅读机器学习论文.书籍过程中所碰到的算法,每篇文章描述一个具体的算法.算法的编程实现.算法的具体应用实例.争取每个算法都用多种语言编程实现.所有代码共享至github:https://github.com/wepe/MachineLearning-Demo     欢迎交流指正! (1)kNN算法_手写识别实例--基于Python和NumPy函数库 1.kNN算法简介 kNN算法,即K最近邻(k-NearestNeighbor)分类算法,是最简单的机器学习算

KNN算法实现数字识别

KNN算法介绍 KNN算法(K-NearestNeighor Algorithm) 是一种最简单的分类算法. 算法核心: 假设在一个二维坐标平面中已经有了\(n\)个点,每个点的颜色已知,现在给定查询点\(p\)的坐标\((x,y)\),判断\(p\)的颜色. 对于已知的\(n\)个点,计算每个点和点\(p\)的欧几里得距离: \[dis_i=\sqrt{(x_i-x)^2+(y_i-y)^2}\] 按照\(dis\)从小到大排序,选择距离最近的前\(k\)个点,在这前k个点中统计颜色出现次数最

Machine Learning In Action 第二章学习笔记: kNN算法

本文主要记录<Machine Learning In Action>中第二章的内容.书中以两个具体实例来介绍kNN(k nearest neighbors),分别是: 约会对象预测 手写数字识别 通过“约会对象”功能,基本能够了解到kNN算法的工作原理.“手写数字识别”与“约会对象预测”使用完全一样的算法代码,仅仅是数据集有变化. 约会对象预测 1 约会对象预测功能需求 主人公“张三”喜欢结交新朋友.“系统A”上面注册了很多类似于“张三”的用户,大家都想结交心朋友.“张三”最开始通过自己筛选的

Opencv学习之路—Opencv下基于HOG特征的KNN算法分类训练

在计算机视觉研究当中,HOG算法和LBP算法算是基础算法,但是却十分重要.后期很多图像特征提取的算法都是基于HOG和LBP,所以了解和掌握HOG,是学习计算机视觉的前提和基础. HOG算法的原理很多资料都可以查到,简单来说,就是将图像分成一个cell,通过对每个cell的像素进行梯度处理,进而根据梯度方向和梯度幅度来得到cell的图像特征.随后,将每个cell的图像特征连接起来,得到一个BLock的特征,进而得到一张图片的特征.Opencv当中自带HOG算法,可以直接调用,进行图像的特征提取.但