一、 Logisitic regression
逻辑回归是机器学习算法中最基础的算法之一,简单的理解逻辑回归就是进行二分类。
其本质就是在空间中找到一个决策边界来完成分类的决策。
举个例子:
上图有一些数据,特征为x1 和 x2,类别为0 或 1,现在我们需要找到一个线来划分这个图,使得这条线上为一个类,线下为另一个类。如下图所示。
这就是Logistic Regression 所能做的事。
二、逻辑回归步骤
- 收集数据
- 准备数据:结构化数据格式,即向量化。
- 分析数据
- 训练数据:尝试找到最佳系数来对数据进行分类。
- 测试数据:一旦训练结束,分类测试数据是快速且容易的事。
- 使用数据:此应用程序需要获取一些输入数据并输出结构化数值。
三、Sigmoid函数
S ( z ) = 1 1 + e − z S(z) = \frac{1}{1+e^{-z}} S(z)=1+e−z1
即当
z
>
0
为
1
,
z
=
0
为
0.5
,
z
<
0
为
0
z > 0 为 1,z = 0 为 0.5,z < 0 为 0
z>0为1,z=0为0.5,z<0为0。
在python中代码如下:
def sigmoid(x):
return 1.0/(1+np.exp(-x))
四、梯度下降算法
这是机器学习算法中最常用的算法之一,通过一步步的迭代从而求得局部最优解。
从任意点开始出发,如上图是从 P0 点开始出发,梯度被计算出来然后到达 P1, P2, P3,直到到达局部最优解。
w : = w − α ∇ w f ( w ) w := w - \alpha\nabla_wf(w) w:=w−α∇wf(w)
其伪代码如下:
S
t
a
r
t
w
i
t
h
t
h
e
w
e
i
g
h
t
s
a
l
l
s
e
t
t
o
1
Start \ with \ the \ weights \ all \ set \ to \ 1
Start with the weights all set to 1
R
e
p
e
a
t
R
n
u
m
b
e
r
o
f
t
i
m
e
s
:
Repeat \ R \ number \ of \ times:
Repeat R number of times:
C
a
l
c
u
l
a
t
e
t
h
e
g
r
a
d
i
e
n
t
o
f
t
h
e
e
n
t
i
r
e
d
a
t
a
s
e
t
\quad Calculate \ the \ gradient \ of \ the \ entire \ dataset
Calculate the gradient of the entire dataset
U
p
d
a
t
e
t
h
e
w
e
i
g
h
t
s
v
e
c
t
o
r
b
y
a
l
p
h
a
∗
g
r
a
d
i
e
n
t
\quad Update \ the \ weights \ vector \ by \ alpha*gradient
Update the weights vector by alpha∗gradient
R
e
t
u
r
n
t
h
e
w
e
i
g
h
t
s
v
e
c
t
o
r
\quad Return \ the \ weights \ vector
Return the weights vector
五、训练过程
5.1 数据导入:
给定数据集(前两列为特征x1, x2,第三列为标签y) 如下所示:
-0.017612 14.053064 0
-1.395634 4.662541 1
-0.752157 6.538620 0
-1.322371 7.152853 0
0.423363 11.054677 0
0.406704 7.067335 1
0.667394 12.741452 0
-2.460150 6.866805 1
0.569411 9.548755 0
-0.026632 10.427743 0
0.850433 6.920334 1
1.347183 13.175500 0
1.176813 3.167020 1
…
def loadDataSet():
# 数据矩阵X
dataMat = []
# 标签矩阵y
labelMat = []
# 打开文件
fr = open('testSet.txt')
# 逐行读取
for line in fr.readlines():
# 去掉每一行首尾的空白符
lineArr = line.strip().split()
#(x1, x2)点数据集`在这里插入代码片`
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
#标签向量
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
5.2 梯度下降
def gradAscent(dataMath, classLabels):
# 转换成numpy的mat(矩阵)
dataMatrix = np.mat(dataMath)
#print(dataMatrix)
# 转换成numpy的mat(矩阵)并进行转置
labelMat = np.mat(classLabels).transpose()
# m为行数,n为列数
m, n = np.shape(dataMatrix)
#学习率
alpha = 0.01
#最大迭代次数
maxCycles = 500
# 初始化权重
weights = np.ones((n,1))
weights_array = np.array([])
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
# 计算真实类别与预测类别的差值
error = labelMat - h
# 调整回归系数
weights = weights + alpha * dataMatrix.transpose() * error
weights_array = np.append(weights_array, weights)
weights_array = weights_array.reshape(maxCycles, n)
# mat.getA()将自身矩阵变量转化为ndarray类型变量
return weights.getA(), weights_array
六、数据可视化
画出数据集和Logistic regression最佳拟合直线的函数
def plotBestFit(weights):
# 加载数据集
dataMat, labelMat = loadDataSet()
# 转换成numpy的array数组
dataArr = np.array(dataMat)
# 数据个数
# 例如建立一个4*2的矩阵c,c.shape[1]为第一维的长度2, c.shape[0]为第二维的长度4
n = np.shape(dataMat)[0]
# 正样本
xcord1 = []
ycord1 = []
# 负样本
xcord2 = []
ycord2 = []
# 根据数据集标签进行分类
for i in range(n):
if int(labelMat[i]) == 1:
# 1为正样本
xcord1.append(dataArr[i, 1])
ycord1.append(dataArr[i, 2])
else:
# 0为负样本
xcord2.append(dataArr[i, 1])
ycord2.append(dataArr[i, 2])
# 新建图框
fig = plt.figure()
# 添加subplot
ax = fig.add_subplot(111)
# 绘制正样本
ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)
# 绘制负样本
ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)
# x轴坐标
x = np.arange(-3.0, 3.0, 0.1)
# w0*x0 + w1*x1 * w2*x2 = 0
# x0 = 1, x1 = x, x2 = y
y = (-weights[0] - weights[1] * x) / weights[2]
ax.plot(x, y)
# 绘制title
plt.title('BestFit')
# 绘制label
plt.xlabel('x1')
plt.ylabel('y2')
# 显示
plt.show()
运行一下结果显示为:
七、随机梯度下降(SGD)
因为梯度下降需要遍历整个数据集取个平均来当做每一次梯度下降的step,一旦数据很多复杂度就很高,所以可以改进为一次仅用一个样本点来更新回归系数,这种方法就叫SGD算法。
对比一下BGD与SGD算法的回归系数更新公式。
BGD为:
SGD为:
可以发现BGD需要一个求和公式,而SGD只是用第
i
i
i 个样本点进行step的求解。
其伪代码如下:
S
t
a
r
t
w
i
t
h
t
h
e
w
e
i
g
h
t
s
a
l
l
s
e
t
t
o
1
Start \ with \ the \ weights \ all \ set \ to \ 1
Start with the weights all set to 1
F
o
r
e
a
c
h
p
i
e
c
e
o
f
d
a
t
a
i
n
t
h
e
d
a
t
a
s
e
t
:
For \ each \ piece \ of \ data \ in \ the \ dataset:
For each piece of data in the dataset:
C
a
l
c
u
l
a
t
e
t
h
e
g
r
a
d
i
e
n
t
o
f
o
n
e
p
i
r
e
c
e
o
f
d
a
t
a
s
e
t
\quad Calculate \ the \ gradient \ of \ one \ pirece \ of \ dataset
Calculate the gradient of one pirece of dataset
U
p
d
a
t
e
t
h
e
w
e
i
g
h
t
s
v
e
c
t
o
r
b
y
a
l
p
h
a
∗
g
r
a
d
i
e
n
t
\quad Update \ the \ weights \ vector \ by \ alpha*gradient
Update the weights vector by alpha∗gradient
R
e
t
u
r
n
t
h
e
w
e
i
g
h
t
s
v
e
c
t
o
r
\quad Return \ the \ weights \ vector
Return the weights vector
如果采用如下代码:
那么分类结果为:
在200次迭代过程中回归系数的变化图如下:
其中 X2 只经过50次迭代就达到了稳定,而 X0 和 X1则需要更多迭代,且存在一些剧烈波动,那么希望算法能避免来回波动,从而收敛到某个值。
改进的SGD代码如下:
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
# 返回dataMatrix的大小,m为行数,n为列数
m, n = np.shape(dataMatrix)
# 参数初始化
weights = np.ones(n)
weights_array = np.array([])
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
# 每次都降低alpha的大小
alpha = 4/(1.0+j+i)+0.01
# 随机选择样本
randIndex = int(random.uniform(0, len(dataIndex)))
# 随机选择一个样本计算h
h = sigmoid(sum(dataMatrix[randIndex] * weights))
# 计算误差
error = classLabels[randIndex] - h
# 更新回归系数
weights = weights + alpha * error * dataMatrix[randIndex]
# 添加返回系数到数组中当axis为0时,数组是加在下面(列数要相同)
weights_array = np.append(weights_array, weights, axis=0)
# 删除已使用的样本
del(dataIndex[randIndex])
# 改变维度
weights_array = weights_array.reshape(numIter*m, n)
# 返回
return weights, weights_array
学习率在每次迭代时都会调整,从而缓解数据波动或者高频波动;此外,通过随机选取样本来更新回归系数,从而减少周期性的波动。
这样最终运行结果和BGD算法效果几乎一样,不再贴图。