机器学习——线性回归
参考:
https://cuijiahua.com/blog/2017/11/ml_6_logistic_1.html
https://cuijiahua.com/blog/2017/11/ml_7_logistic_2.html
一、线性回归前置简介
1.1、Sigmoid函数
优点:
- Sigmoid函数的输出映射在(0,1)之间,单调连续,输出范围有限,优化稳定,可以用作输出层。
- 求导容易。
缺点:
- 由于其软饱和性,容易产生梯度消失,导致训练出现问题。
- 其输出并不是以0为中心的。
这 Sigmoid 函数作为**函数,x 可以代表很多参数,例如:单个数字、矩阵
则 Sigmoid 函数就如下图所示:
其中:z是一个矩阵,θ是参数列向量(要求解的),x是样本列向量(给定的数据集)。θ^T表示θ的转置。
1.2、损失函数
根据sigmoid函数的特性,我们可以做出如下的假设:
在已知样本x和参数θ的情况下,样本x属性正样本(y=1)和负样本(y=0)的条件概率。理想状态下,根据上述公式,求出各个点的概率均为1,也就是完全分类都正确。
但是考虑到实际情况,样本点的概率越接近于1,其分类效果越好。比如一个样本属于正样本的概率为0.51,那么我们就可以说明这个样本属于正样本。另一个样本属于正样本的概率为0.99,那么我们也可以说明这个样本属于正样本。但是显然,第二个样本概率更高,更具说服力。我们可以把上述两个概率公式合二为一:
合并出来的Loss,我们称之为损失函数(Loss Function)。当y等于1时,(1-y)项(第二项)为0;当y等于0时,y项(第一项)为0。
为s了简化问题,我们对整个表达式求对数,(将指数问题对数化是处理数学问题常见的方法):
这个损失函数,是对于一个样本而言的。给定一个样本,我们就可以通过这个损失函数求出,样本所属类别的概率,而这个概率越大越好,所以也就是求解这个损失函数的最大值。既然概率出来了,那么最大似然估计也该出场了。假定样本与样本之间相互独立,那么整个样本集生成的概率即为所有样本生成概率的乘积,再将公式对数化,便可得到如下公式:
其中,m为样本的总数,y(i)表示第i个样本的类别,x(i)表示第i个样本,需要注意的是θ是多维向量,x(i)也是多维向量。
1.3、梯度上升算法
在我们用数学求解时,自然使用到求导 f’(x)=-2x+4,然后使其等于0,我们就可以得到最大值时的 x=2。
但是真实环境中的函数不会像上面这么简单,就算求出了函数的导数,也很难精确计算出函数的极值。此时我们就可以用迭代的方法来做。就像爬坡一样,一点一点逼近极值。这种寻找最佳拟合参数的方法,就是最优化算法。爬坡这个动作用数学公式表达即为:
编程实现:
# !/usr/bin/python
# -*- coding: utf-8 -*-
# @Time : 2020/1/6 16:26
# @Author : ljf
# @File : LOG_test1.py
def f_prime(x_old): # f(x)的导数
return -2 * x_old + 4
def Gradient_Ascent_test():
"""
函数说明:梯度上升算法测试函数
求函数f(x) = -x^2 + 4x的极大值
Returns:
无
"""
x_old = -1 # 初始值,给一个小于x_new的值
x_new = 0 # 梯度上升算法初始值,即从(0,0)开始
alpha = 0.01 # 步长,也就是学习速率,控制更新的幅度
presision = 0.00000001 # 精度,也就是更新阈值
while abs(x_new - x_old) > presision:
x_old = x_new
x_new = x_old + alpha * f_prime(x_old) # 上面提到的公式
print(x_new) # 打印最终求解的极值近似值
if __name__ == '__main__':
Gradient_Ascent_test()
1.4、J(θ)的求导
最后就是梯度上升迭代公式。
二、示例
示例就是把两种点用直线分类,求这条直线。
1、数据准备
https://github.com/Jack-Cherish/Machine-Learning/blob/master/Logistic/testSet.txt
2、步骤
把数据文件下载下来。
- 加载数据,并初步处理
- 利用梯度上升迭代公式计算出每一列的权重
- 根据每列权重画出直线
3、改进
因为这个梯度上升迭代公式,每次修改权重时,必须每次全部遍历,这样就会耗费很多时间和资源。
所以有了改进,每次修改随机挑取一个向量进行计算。
4、代码
# !/usr/bin/python
# -*- coding: utf-8 -*-
# @Time : 2020/1/6 21:17
# @Author : ljf
# @File : LOG_test5.py
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
import numpy as np
import random
def loadDataSet():
"""
函数说明:加载数据
Returns:
dataMat: 数据列表
labelMat: 标签列表
"""
dataMat = [] # 创建数据列表
labelMat = [] # 创建标签列表
fr = open('testSet.txt') # 打开文件
for line in fr.readlines(): # 逐行读取
lineArr = line.strip().split() # 去回车,放入列表
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 添加数据
labelMat.append(int(lineArr[2])) # 添加标签
fr.close() # 关闭文件
return dataMat, labelMat # 返回
def sigmoid(inX):
"""
函数说明:sigmoid函数
Args:
inX: 数据
Returns:
sigmoid函数
"""
return 1.0 / (1 + np.exp(-inX))
def gradAscent(dataMatIn, classLabels):
"""
函数说明:梯度上升算法
Args:
dataMatIn: 数据集
classLabels: 数据标签
Returns:
weights.getA(): 求得的权重数组(最优参数)
"""
dataMatrix = np.mat(dataMatIn) # 转换成numpy的mat
labelMat = np.mat(classLabels).transpose() # 转换成numpy的mat,并进行转置
m, n = np.shape(dataMatrix) # 返回dataMatrix的大小。m为行数,n为列数。
alpha = 0.001 # 移动步长,也就是学习速率,控制更新的幅度。
maxCycles = 500 # 最大迭代次数
weights = np.ones((n, 1))
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights) # 梯度上升矢量化公式
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * error
return weights.getA() # 将矩阵转换为数组,返回权重数组
def plotBestFit(weights):
"""
函数说明:绘制数据集
Args:
weights: 权重参数数组
Returns:
无
"""
dataMat, labelMat = loadDataSet() # 加载数据集
dataArr = np.array(dataMat) # 转换成numpy的array数组
n = np.shape(dataMat)[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]) # 1为正样本
else:
xcord2.append(dataArr[i, 1])
ycord2.append(dataArr[i, 2]) # 0为负样本
fig = plt.figure()
ax = fig.add_subplot(111) # 添加subplot
ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5) # 绘制正样本
ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5) # 绘制负样本
x = np.arange(-3.0, 3.0, 0.1)
y = (-weights[0] - weights[1] * x) / weights[2]
ax.plot(x, y)
plt.title('BestFit') # 绘制title
plt.xlabel('X1')
plt.ylabel('X2') # 绘制label
plt.show()
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
"""
函数说明:改进的随机梯度上升算法
Args:
dataMatrix: 数据数组
classLabels: 数据标签
numIter: 迭代次数
Returns:
weights: 求得的回归系数数组(最优参数)
"""
m, n = np.shape(dataMatrix) # 返回dataMatrix的大小。m为行数,n为列数。
weights = np.ones(n) # 参数初始化
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
alpha = 4 / (1.0 + j + i) + 0.01 # 降低alpha的大小,每次减小1/(j+i)。
randIndex = int(random.uniform(0, len(dataIndex))) # 随机选取样本
h = sigmoid(sum(dataMatrix[dataIndex[randIndex]] * weights)) # 选择随机选取的一个样本,计算h
error = classLabels[dataIndex[randIndex]] - h # 计算误差
weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]] # 更新回归系数
del (dataIndex[randIndex]) # 删除已经使用的样本
return weights # 返回
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
weights = stocGradAscent1(np.array(dataMat), labelMat)
print(weights)
plotBestFit(weights)
三、总结
3.1、Logistic回归的一般过程
- 收集数据:采用任意方法收集数据。
- 准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳。
- 分析数据:采用任意方法对数据进行分析。
- 训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
- 测试算法:一旦训练步骤完成,分类将会很快。
- 使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数,就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。
3.2、重要
梯度上升迭代公式