线性分类器
给定一些数据集合,他们分别属于两个不同的类别。例如对于广告数据来说,是典型的二分类问题,一般将被点击的数据称为正样本,没被点击的数据称为负样本。现在我们要找到一个线性分类器,将这些数据分为两类(当然实际情况中,广告数据特别复杂,不可能用一个线性分类器区分)。用X表示样本数据,Y表示样本类别(例如1与-1,或者1与0)。我们线性分类器的目的,就是找到一个超平面(Hyperplan)将两类样本分开。对于这个超平面,可以用以下式子描述:
对于logistic回归,有:
其中 xx 为样本,x=[x1,x2,⋯,xn]x=[x1,x2,⋯,xn] 为n维向量,函数g为我们常说的logistic函数。g的更一般公式为:
** g(z)图像 **
图像优点
- 他的输入范围是−∞→+∞ ,而之于刚好为(0,1),正好满足概率分布为(0,1)的要求。
- 他是一个单调上升的函数,具有良好的连续性,不存在不连续点。
g(z)函数求导
损失函数
https://blog.youkuaiyun.com/bitcarmanlee/article/details/51165444
对损失函数求导得出迭代公式
J(θ)=cost(hθ(x),y)=−yilog(hθ(x))−(1−yi)log(1−hθ(x))
则:
迭代公式向量化
样本矩阵表示:
将x*θ的乘积记为:
h(x)-y表示为
对于迭代公式,令j = 0
矩阵表达式
即
处理数据
def loadDataSet(filename):
'''
加载数据集
:param filename: 数据集名称
:return:
dataMat : 文本数据集 类型 list
labelMat: 标签数据集 类型 list
'''
dataMat = []
labelMat = []
fr = open(filename)
for line in fr.readlines():
lineArr = line.strip().split()
if len(lineArr) == 1:
continue
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
sigmoid函数
def sigmoid(inX):
'''
sigmoid: s型曲线图像
tanh:类s型曲线图像 双曲正切
:param inX: 参数X
:return: 函数值Y
'''
return 2*1.0/(1+exp(-2*inX))-1
阶梯上升法
def gradAscent(dataMatIn,classLabels):
'''
:param dataMatIn: 文本集数组
:param classLabels: 标签集列表
:return:
'''
#转化为numpy矩阵
dataMatrix = mat(dataMatIn)
#转化为numpy矩阵并转置
labelMat = mat(classLabels).transpose()
#获取数据矩阵形状 m 文本数据量 n 特征量
m, n = shape(dataMatrix)
#步长
alpha = 0.001
#迭代次数
maxCycles = 500
#初始化回归系数
weights = ones((n,1))
for k in range(maxCycles):
# h: m*1矩阵
h = sigmoid(dataMatrix*weights)
# error表示每一列上的误差情况
error = (labelMat - h)
#alpha * dataMatrix.transpose()*error表示 每一列的误差情况
weights = weights + alpha * dataMatrix.transpose()*error
return weights
接下来是随机梯度上升法,随机挑选一个样本进行梯度分析
def stocGradAscent0(dataMatrix,classLabels):
'''
随机梯度上升法
:param dataMatrix: 文本集数组
:param classLabels: 标签集列表
:return:回归系数
'''
#获取形状
m, n = shape(dataMatrix)
alpha = 0.01
#初始化矩阵
weights = ones(n)
for i in range(m):
#求出f(x)的值
h = sigmoid(sum(dataMatrix[i]* weights))
#计算真实类别与预测类别之间的差值,调整回归系数
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i]
return weights
由于处理数据时 可能遇到较大量数据,这时我们再一个一个处理数据会有较高的时间复杂度,批量化处理数据就是一个方法
def stocGradAscent1(dataMatrix, classLabels, numIter = 150):
'''
改进版的随机梯度下降,使用随机的一个样本来更新回归系数
:param dataMatrix:
:param classLabels:
:param numIter: 迭代次数
:return: weights 回归系数
'''
m, n = shape(dataMatrix)
weights = ones(n)
#随机梯度,循环150,观察是否收敛
for j in range(numIter):
dataIndex = [x for x in range(m)]
for i in range(m):
alpha = 4/(1.0 + j + i)+ 0.0001
randIndex = int(random.uniform(0,len(dataIndex)))
h = sigmoid(sum(dataMatrix[dataIndex[randIndex]]* weights))
error = classLabels[dataIndex[randIndex]] - h
weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]
del (dataIndex[randIndex])
return weights
数据可视化
def plotBestFit(dataArr, labelMat, weights):
'''
数据可视化
:param dataArr:
:param labelMat:
:param weights:
:return: none
'''
n = shape(dataArr)[0]
xcord1 = []
ycord1 = []
xcord2 = []
ycord2 = []
for i in range(n):
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i, 1])
ycord1.append(dataArr[i, 2])
else:
xcord2.append(dataArr[i, 1])
ycord2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, s=30, c='red', marker = 's')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = arange(-3.0, 3.0, 0.1)
'''
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
w0*x0+w1*x1+w2*x2=f(x)
w0+w1*x+w2*y=0 => y = (-w0-w1*x)/w2
'''
y = (-weights[0]-weights[1]*x)/weights[2]
ax.plot(x,y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
测试算法
def simpleTest():
dataMat, labelMat = loadDataSet('../../testData/Logistic/TestSet.txt')
dataArr = array(dataMat)
weights = stocGradAscent1(dataArr, labelMat)
# weights = stocGradAscent0(dataArr,labelMat)
plotBestFit(dataArr, labelMat, weights)