机器学习入门实践--线性模型-分类算法-二分类问题(python)
本博客的理论细节在这里:机器学习入门-分类问题的拟合本博客侧重于实现,细节理论不再过多赘述,只简单介绍。逻辑回归理论分析逻辑回归就是一个二分类问题的分类算法,其根据得出的回归函数可以分为线性逻辑回归和非线性逻辑回归。1.确定拟合函数h(x)要拟合的函数有两个,分别是线性逻辑回归函数和非线性逻辑回归函数线性逻辑回归对于超平面来说,线性回归的模型是h(x)=kx+b这个k,x,b都可以是多维的,只需要
本博客的理论细节在这里:机器学习入门-分类问题的拟合
本博客侧重于实现,细节理论不再过多赘述,只简单介绍。
线性模型-二分类问题理论分析
线性回归是一个处理二分类问题的常见分类算法,其得出的回归函数为线性模型回归函数。
二分类问题:给出一个参数x集合,根绝这些参数预测其结果是0或1.
1.确定拟合函数h(x)
要拟合的函数有两种,分别是线性回归函数和逻辑回归函数
- 线性回归
对 于 超 平 面 来 说 , 线 性 回 归 的 模 型 是 h ( x ) = k x + b 这 个 k , x , b 都 可 以 是 多 维 的 , 只 需 要 满 足 k x 线 性 相 乘 即 可 。 其 实 , 将 x 增 加 一 个 值 为 1 的 维 度 , k 和 b 合 成 一 个 向 量 组 , 就 可 以 写 成 矩 阵 相 乘 的 形 式 : 即 : θ = [ b , k ] T , x = [ 1 , x ] T 即 : h θ ( x ) = θ T x 对于超平面来说,线性回归的模型是h(x)=kx+b\\ 这个k,x,b都可以是多维的,只需要满足kx线性相乘即可。\\ 其实,将x增加一个值为1的维度,k和b合成一个向量组,就可以写成矩阵相乘的形式:\\ 即:\theta=[b,k]^T,x=[1,x]^T\\ 即:h_\theta(x)=\theta^T x 对于超平面来说,线性回归的模型是h(x)=kx+b这个k,x,b都可以是多维的,只需要满足kx线性相乘即可。其实,将x增加一个值为1的维度,k和b合成一个向量组,就可以写成矩阵相乘的形式:即:θ=[b,k]T,x=[1,x]T即:hθ(x)=θTx
- 线性回归的非线性映射(逻辑回归)
- 例如最经典的分类问题(0,1)
对 于 非 线 性 回 归 , 通 常 使 用 h ( x ) = 1 1 + e − ( k x + b ) 来 表 示 同 理 , k , x , b 都 可 以 是 多 维 的 , 用 向 量 组 来 表 示 : h θ ( x ) = 1 1 + e − θ T x y = 1 i f h θ ( x ) > 0.5 y = 0 i f h θ ( x ) < 0.5 y = 0 o r 1 i f h θ ( x ) = 0.5 对于非线性回归,通常使用h(x)=\frac{1}{1+e^{-(kx+b)}}来表示\\ 同理,k,x,b都可以是多维的,用向量组来表示:\\ h_\theta(x)=\frac{1}{1+e^{-\theta^Tx}}\\ y=1\quad if\ h_\theta(x)>0.5\\ y=0\quad if\ h_\theta(x)<0.5\\ y=0\ or\ 1\quad if\ h_\theta(x)=0.5 对于非线性回归,通常使用h(x)=1+e−(kx+b)1来表示同理,k,x,b都可以是多维的,用向量组来表示:hθ(x)=1+e−θTx1y=1if hθ(x)>0.5y=0if hθ(x)<0.5y=0 or 1if hθ(x)=0.5
其实本质仍是线性回归,只是它的h(x)的映射区间变为了非线性
2.确定损失函数J(θ)
损失函数也是较好确定的,计算拟合函数h(x)和真实值的距离即可。
//这里对于非线性回归使用了交叉熵损失函数优化,不细说了。
很
明
显
,
对
于
单
个
样
本
的
损
失
函
数
:
J
(
θ
)
=
C
o
s
t
(
h
θ
(
x
)
,
y
)
=
−
log
(
h
θ
(
x
)
)
i
f
k
=
1
J
(
θ
)
=
C
o
s
t
(
h
θ
(
x
)
,
y
)
=
−
log
(
1
−
h
θ
(
x
)
)
i
f
k
=
0
合
并
起
来
就
是
:
J
(
θ
)
=
C
o
s
t
(
h
θ
(
x
)
,
y
)
=
−
(
log
(
h
θ
(
x
)
)
y
+
log
(
1
−
h
θ
(
x
)
)
(
1
−
y
)
)
\begin{aligned} & 很明显,对于单个样本的损失函数:\\ & J(\theta)=Cost(h_\theta(x),y)=-\log (h_\theta(x))\qquad\qquad if\ k=1\\ & J(\theta)=Cost(h_\theta(x),y)=-\log(1-h_\theta(x))\qquad if\ k=0\\ & 合并起来就是:\\ & J(\theta)=Cost(h_\theta(x),y)=-(\log(h_\theta(x))y+\log(1-h_\theta(x))(1-y))\\ \end{aligned}
很明显,对于单个样本的损失函数:J(θ)=Cost(hθ(x),y)=−log(hθ(x))if k=1J(θ)=Cost(hθ(x),y)=−log(1−hθ(x))if k=0合并起来就是:J(θ)=Cost(hθ(x),y)=−(log(hθ(x))y+log(1−hθ(x))(1−y))
从单样本推广到多样本:
极大似然估计法此处不详细说明
对
于
多
个
训
练
样
本
{
x
(
i
)
,
y
(
i
)
}
来
说
,
用
极
大
似
然
估
计
法
,
其
训
练
集
的
代
价
函
数
为
:
J
(
θ
)
=
−
1
m
∑
i
=
1
m
C
o
s
t
(
h
θ
(
x
(
i
)
)
,
y
(
i
)
)
=
−
1
m
∑
i
=
1
m
[
log
(
h
θ
(
x
(
i
)
)
)
y
(
i
)
+
log
(
1
−
h
θ
(
x
(
i
)
)
)
(
1
−
y
(
i
)
)
]
\begin{aligned} & 对于多个训练样本\{x^{(i)},y^{(i)}\}来说,用极大似然估计法,其训练集的代价函数为:\\ & J(\theta)=-\frac{1}{m}\sum_{i=1}^mCost(h_\theta(x^{(i)}),y^{(i)})=-\frac{1}{m}\sum_{i=1}^m[\log(h_\theta(x^{(i)}))y^{(i)}+\log(1-h_\theta(x^{(i)}))(1-y^{(i)})]\\ \end{aligned}
对于多个训练样本{x(i),y(i)}来说,用极大似然估计法,其训练集的代价函数为:J(θ)=−m1i=1∑mCost(hθ(x(i)),y(i))=−m1i=1∑m[log(hθ(x(i)))y(i)+log(1−hθ(x(i)))(1−y(i))]
我们求出合适的k,b来获得最小的损失函数J(θ)
3.使用梯度下降求解
梯度下降就是求导,通过找到求导找到损失函数最快下降的方向,逐渐逼近最优的L(k,b)。
(这里涉及到同步更新和异步更新的选择,一般来讲,我们选择同步更新)
根
据
梯
度
下
降
算
法
,
更
新
每
一
项
θ
,
使
其
延
代
价
函
数
对
θ
的
切
线
方
向
(
偏
导
数
)
下
降
θ
j
:
=
θ
j
−
α
∂
∂
θ
j
J
(
θ
)
求
导
:
θ
j
:
=
θ
j
−
α
∂
(
1
m
[
∑
i
=
1
m
y
(
i
)
l
o
g
h
θ
(
x
(
i
)
)
+
(
1
−
y
(
i
)
)
l
o
g
(
1
−
h
θ
(
x
(
i
)
)
)
]
)
∂
θ
j
θ
j
:
=
θ
j
−
α
(
y
(
i
)
−
h
θ
(
x
(
i
)
)
)
x
(
i
)
\begin{aligned} & 根据梯度下降算法,更新每一项\theta,使其延代价函数对\theta的切线方向(偏导数)下降\\ &\qquad \theta_j:=\theta_j-\alpha\frac{\partial}{\partial\theta_j}J(\theta)\\ & 求导:\\ &\qquad \theta_j:=\theta_j-\alpha\frac{\partial (\frac{1}{m}[\sum_{i=1}^my^{(i)}logh_{\theta}(x^{(i)})+(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))])}{\partial\theta_j} \\ &\qquad\theta_j :=\theta_j-\alpha (y^{(i)}-h_\theta(x^{(i)})) x^{(i)} \end{aligned}
根据梯度下降算法,更新每一项θ,使其延代价函数对θ的切线方向(偏导数)下降θj:=θj−α∂θj∂J(θ)求导:θj:=θj−α∂θj∂(m1[∑i=1my(i)loghθ(x(i))+(1−y(i))log(1−hθ(x(i)))])θj:=θj−α(y(i)−hθ(x(i)))x(i)
详细化简步骤:
当取log的e为底时lne=1,可以忽略掉。
自此,我们已经基本明确了如何拟合,确定损失函数,使用梯度下降求取θ
4.确定算法执行过程
S t e p 1 : 随 机 选 择 一 个 梯 度 θ S t e p 2 : 重 复 进 行 梯 度 下 降 求 取 θ 确 定 步 长 α 同 步 更 新 θ j : = θ j − α ∂ ∂ θ j J ( θ j ) S t e p 3 : 找 到 合 适 的 θ 终 止 循 环 \begin{aligned} & Step1:随机选择一个梯度\theta\\ & Step2:重复进行梯度下降求取\theta\\ &\qquad 确定步长\alpha\\ &\qquad 同步更新\theta_j:=\theta_j-\alpha\frac{\partial}{\partial\theta_j}J(\theta_j)\\ & Step3:找到合适的\theta终止循环 \end{aligned} Step1:随机选择一个梯度θStep2:重复进行梯度下降求取θ确定步长α同步更新θj:=θj−α∂θj∂J(θj)Step3:找到合适的θ终止循环
线性模型-二分类问题python实践
根据以上的分析,我们依次进行实现。
1.拟合函数h(x)实现
我们只需要对着上面的h(x)进行编写即可。
新建一个文件:LogisticRegressionTrain.py,用作训练逻辑回归。
在其中先导入numpy库(用到了自然对数e和矩阵乘法),后写入拟合函数h(x)。
import numpy as np
def forecast(theta,x):
'''
forecast()函数,用来求x的预测值
输入:theta预测模型((n+1)*1表示n+1个特征)
x样本(m*(n+1)表示m行n+1列,m个样本,每个样本有n+1个特征,多出来的是作为常数项1)
输出:forecastValue预测值(m*1)的一个数组
'''
forecastValue=1.0/(1+np.exp(-(x*theta)))#直接向量点乘获取预测值矩阵
return forecastValue
#-----------下面用一个简单的样例测试一下是否输出成功---------------
x=np.mat([[1],[-1]])#样本x
theta=np.mat([1])#预测模型
y=np.mat([[0],[1]])#样本y
fc=forecast(theta,x)
print(fc)
'''输出结果
[[0.73105858]
[0.26894142]]
'''
- np.exp(x)表示e^x
- np.dot(a,b)表示a和b的点乘(即a点乘b),也可以用矩阵乘积表示如a*b表示矩阵a点乘矩阵b
- np.mat([])创建一个自定义的矩阵
2.损失函数J(θ)实现
在拟合函数下面继续写损失函数
def errorRate(y,fc):
'''
errorRate()函数,用来求损失函数
输入:y样本值(m*1的一个矩阵,表示m个样本的样本值)
fc预测值(m*1的一个矩阵,表示m个样本的预测值)
输出:损失函数值
'''
m=np.shape(y)[0]#获得样本数量m
sum_err=0.0#记录误差值之和
for i in range(m):
sum_err+=(np.log(fc[i,0])*y[i,0])+(np.log(1-fc[i,0])*(1-y[i,0]))
sum_err/=m
return sum_err
#-----------下面用一个简单的样例测试一下是否输出成功---------------
x=np.mat([[1],[-1]])#样本x
theta=np.mat([1])#预测模型
y=np.mat([[0],[1]])#样本y
fc=forecast(theta,x)
print(fc)
errrate=errorRate(y,fc)
print(errrate)
'''输出
[[0.73105858]
[0.26894142]]
-1.3132616875182228
'''
- np.log(x)表示取x的对数,底数为e。
- for i in range(m)表示遍历从0到m-1.
- np.shape(x)返回一个元组(m,n)表示m行n列,可以用下表获取其值[0]表示获取m,[1]表示获取n
- a.reshape(m,n)表示将a数组变为m*n的数组
3.使用梯度下降求解
根据前文的理论分析(梯度下降求解和算法执行步骤)。写成算法执行代码。
继续在损失函数下写循环梯度下降的代码
def loopGradientDescent(alpha,y,x,cnt):
'''
loopGradientDescent()函数,用来循环梯度下降求最小的损失函数
输入:
alpha学习率,可以考虑采用动态步长
y样本值(m*1的一个数组,表示m个样本值)
x样本(m*(n+1)的一个数组,表示m个样本,n+1个特征)
cnt最大迭代次数
输出:模型theta((n+1)*1的一个数组,表示n+1个特征的值)
'''
n=np.shape(x)[1]#获取特征数量n+1
m=np.shape(x)[0]#获取样本数量m
theta=np.mat(np.ones((n,1)))#初始化训练模型
for i in range(cnt):
#print(np.shape(theta),np.shape(x))
fc=forecast(theta,x)#获取预测值矩阵(m*1)
#这里直接使用矩阵乘法一次性计算出来(y-fc)*x矩阵(n*1)
theta=theta+alpha*((x.T)*(y-fc)).T#化简后的梯度下降
if i%100==0:#每100次看一下效果
errrate=errorRate(y,fc)
print("第"+str(i)+"次循环,误差率:"+str(errrate))
print("theta:")
print(theta)
return theta
#-----------下面用一个简单的样例测试一下是否输出成功---------------
x=np.mat([[1],[-1]])#样本x
theta=np.mat([1])#预测模型
y=np.mat([[0],[1]])#样本y
print(loopGradientDescent(0.01,y,x,1000))
'''输出
第0次循环,误差率:-1.3132616875182228
theta:
[[0.98537883]]
第100次循环,误差率:-0.6081473016504946
theta:
[[-0.18701442]]
第200次循环,误差率:-0.33968882966892394
theta:
[[-0.9108378]]
第300次循环,误差率:-0.22336178898792158
theta:
[[-1.38920714]]
第400次循环,误差率:-0.16291489229543835
theta:
[[-1.73497099]]
第500次循环,误差率:-0.12697105578355802
theta:
[[-2.00202377]]
第600次循环,误差率:-0.10348538022891132
theta:
[[-2.21810228]]
第700次循环,误差率:-0.0870689750980542
theta:
[[-2.39887204]]
第800次循环,误差率:-0.07500562145864893
theta:
[[-2.55390024]]
第900次循环,误差率:-0.0657951431074245
theta:
[[-2.68940486]]
[[-2.80849509]]
第1000次直接跳出循环了,因此没有输出。
'''
可见误差率在逐渐减小。
- np.mat()得到一个矩阵,自己定义
- np.ones(m,n)得到一个m*n的全1矩阵
- 矩阵相乘=点乘。矩阵加减=对应元素加减。常数*矩阵=常数*矩阵中的每个元素
- str(x)将x变为字符串
以上就是对逻辑回归的所有算法代码了。
接下来要实现的就是数据存取,模型存取,图表绘画等问题。
数据存取与模型存取
我们当然不能在python中手动一个个打进去样本,因此我们采用文件读取的方式来简化样本的输入输出。
使用python提供的io方法。
1.数据获取
随机生成数据
通过随机数据生成器,生成了一些数据,数据样本的格式为{x1,x2,y}
虽然是随机生成的数据,但是我们还要大概划分一下区域的,不能乱生成,否则也找不到线性可分的地方
同目录下新建一个randomData.py的文件,用来生成随机数据
import numpy as np
import random
def randomTrainData(m,n):
'''
randomData()函数生成随机数据,m组样本,每组样本n个特征值
输入:m表示m个样本
n表示n和特征
输出:(m*(n+1))的一个样本矩阵,m个样本,n个特征+1个样本值
'''
dataSet0=[]#样本0
dataSet1=[]#样本1
for i in range(n):#n个特征,每个特征m个样本
tmpData=np.random.normal(2,1.2,m)#均值为2,方差为1.2,生成m个,返回一个列表
dataSet0.append(tmpData)#将特征放进去
tmpData=[]
for i in range(m):#将样本值0放入数组
tmpData.append(0)
dataSet0.append(tmpData)
for i in range(n):#将特征值放入数组
tmpData=np.random.normal(8,1.2,m)
dataSet1.append(tmpData)
tmpData=[]
for i in range(m):#将样本值1放入数组
tmpData.append(1)
dataSet1.append(tmpData)
#得到两个n*m的数组dataSet0和dataSet1,转置后组合在一起
dataSet0=(np.mat(dataSet0)).T
dataSet1=(np.mat(dataSet1)).T
#使用numpy.concatenate()函数,可以将两个列数相同的矩阵拼接起来(m1*n)(m2*n)拼后为(m1+m2)*n
dataSet=[]
dataSet.append(dataSet0)
dataSet.append(dataSet1)
dataSet=np.concatenate(dataSet)
print(dataSet)
return dataSet
def saveTrainData(fileName,dataSet):
'''
saveTrainData()函数将随机到的训练数据存入本地
输入:fileName保存文件名
dataSet一个m*n的矩阵,就是我们得到的训练集
'''
f=open(fileName,"w")
m=np.shape(dataSet)[0]
n=np.shape(dataSet)[1]
for i in range(m):
tmp=[]
for j in range(n):
tmp.append(str(dataSet[i,j]))
f.write("\t".join(tmp))
f.write("\n")
f.close()
if __name__=="__main__":
dataSet=randomTrainData(5,2)#生成10个样本,特征值为2个
saveTrainData("trainData",dataSet)
这样我们就可以得到一个文件名为trainData的数据集了。
更直观上了解点集的分布
如果特征值较少,可以画出点集的分布,在python中做出散点图,这样可以更加直观的看出来我们的数据分布了
在同一目录下新建一个showPicture.py的文件。
读取数据+绘画
import numpy as np
import matplotlib.pyplot as plt
def loadData(fileName):
'''
loadData()从训练集中读出数据,并分类set0和set1
输入:fileName文件名
输出:两个m*n的矩阵,m个样本,n个特征
'''
f=open(fileName)
pointSet0=[]
pointSet1=[]
for line in f.readlines():
tmpPointX=[]
tmpPointY=[]
lines=line.strip().split("\t")#用\t划分字符串
for i in range(len(lines)-1):#提取特征值
tmpPointX.append(float(lines[i]))
tmpPointY.append(float(lines[-1]))#确定分类
if tmpPointY[0] == 0:#分类0放入set0中
pointSet0.append(tmpPointX)
else:#分类1放入set1中
pointSet1.append(tmpPointX)
return np.mat(pointSet0),np.mat(pointSet1)
def drawPicture(pointSet0,pointSet1,xlen,ylen):
'''
drawPicture()函数通过输入的点和长度,画出散点图
输入:pointSet0一个m*n的矩阵,表示m个样本,n个特征
pointSet1一个m*n的矩阵,表示m个样本,n个特征
xlen一个两个变量的列表,[最小值,最大值]
ylen一个两个变量的列表,[最小值,最大值]
'''
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
#matplotlib画图中中文显示会有问题,需要这两行设置默认字体
plt.xlabel('X1')
plt.ylabel('X2')
plt.xlim(xmax=xlen[1],xmin=xlen[0])
plt.ylim(ymax=ylen[1],ymin=ylen[0])
#画两条(-1 - 12)的坐标轴并设置轴标签x1,x2
colors1 = '#00CED1' #点的颜色
colors2 = '#DC143C'
area = np.pi * 2**2 # 点面积
# 画散点图
m=np.shape(pointSet0)[0]
n=np.shape(pointSet0)[1]
for i in range(m):#画出点集0
if i==0:#只对第一个画label
plt.scatter(pointSet0[i,0],pointSet0[i,1],c=colors1,alpha=0.4,label='0')
else:
plt.scatter(pointSet0[i,0],pointSet0[i,1],c=colors1,alpha=0.4)
m=np.shape(pointSet1)[0]
for i in range(m):#画出点集1
if i==0:
plt.scatter(pointSet1[i,0],pointSet1[i,1],c=colors2,alpha=0.4,label='1')
else:
plt.scatter(pointSet1[i,0],pointSet1[i,1],c=colors2,alpha=0.4)
plt.legend()
#plt.savefig(r'C:\Users\Administration\Desktop\tmp.png', dpi=300)#如果要保存的话
plt.show()
if __name__=="__main__":
#获取训练样本的两种点集
pointSet0,pointSet1=loadData("trainData.txt")
print(pointSet0)
print(pointSet1)
#将其画在画布上
drawPicture(pointSet0,pointSet1,[-1,12],[-1,12])
可以看出来,我们的数据分布还是挺不错的
我随机出来的数据在文末的git仓库中,可以自行下载比较
2.数据存取
训练模型之前首先要获得数据,假设我的数据文件trainData.txt放在同目录下。
继续在上面的逻辑回归核心代码下面写文件存取
里面用到的文件io直接用python的内置库即可,无需再导入其他库
def dataLoad(fileName):
'''
dataLoad()函数用来通过文件名来获取其中的数据
输入:fileName样本集保存的文件名
输出:x矩阵(m*(n+1)),m个样本,n个x特征,1个常数项特征
y矩阵(m*1),m个样本,1个样本y
'''
f=open(fileName)#打开样本文件
x=[]
y=[]
for line in f.readlines():#遍历每一行样本
tmpx=[]
tmpy=[]
lines=line.strip().split("\t")#将一行中的样本用tab键分开
tmpx.append(1)#加入一个常数项的特征
for i in range(len(lines)-1):#遍历所有特征值,将其加入x(注意最后一个是y)
tmpx.append(float(lines[i]))
tmpy.append(float(lines[-1]))#最后一个特征值为y
x.append(tmpx)#将列表x放进去
y.append(tmpy)#将列表y放进去
f.close()
return np.mat(x),np.mat(y)
#-------用个例子演示一下-------
x,y=dataLoad("trainData.txt")
print(x)
print(y)
#能正常输出就行了,太长了就不粘上去了。
- f=open(string)默认只读的方式打开一个文件,之后通过f操作文件。可以在后面加入r,w等更改打开方式
- f.close()关闭文件io
- np.append(arr,x)表示向数组arr后插入x
- np.delete(arr,x)表示删除数组中下标为x的值
- strip([chars])在字符串上执行lstrip()和rstrip()
- lstrip()删除字符串左边的空格或指定字符
- rstrip()删除字符串末尾的空格或指定字符
- split(str="")以指定元素为分隔符分割字符串
- float(string)将字符串变为小数
3.模型存取
执行完回归学习后,需要将得到的回归模型持久化。
该模型只需要将参数θ保存在本地即可
def saveModel(fileName,theta):
'''
saveModel()将模型保存到fileName的文件中。
输入:fileName保存的文件名吗,一个字符串
theta模型(n*1)的一个矩阵
'''
n=np.shape(theta)[0]#获取特征数
f=open(fileName,"w")
theta_arr=[]
for i in range(n):
theta_arr.append(str(theta[i,0]))#将特征值转为字符串存入列表
f.write("\t".join(theta_arr))#用tab符号构成间隔
f.close()
#-------用个例子演示一下-------
x,y=dataLoad("trainData.txt")
print(x)
print(y)
theta=loopGradientDescent(0.01,y,x,1000)
print("最终模型")
print(theta)
saveModel("thetaModel",theta)
'''结果根据随机出来的样本数据不同而不同,贴上我的数据结果
第0次循环,误差率:-6.350950611452785
theta:
[[ 0.00848196]
[-3.10447915]
[-6.56845783]]
第100次循环,误差率:-0.0011321139500724707
theta:
[[ 1.20088305]
[ 3.78880061]
[-4.02240082]]
第200次循环,误差率:-0.0009462363867263863
theta:
[[ 1.23612421]
[ 3.91943948]
[-4.15899666]]
第300次循环,误差率:-0.0008139309045824767
theta:
[[ 1.2655657 ]
[ 4.02977837]
[-4.27434446]]
第400次循环,误差率:-0.0007148124403139913
theta:
[[ 1.29081686]
[ 4.12536189]
[-4.37424773]]
第500次循环,误差率:-0.000637704584139752
theta:
[[ 1.31290003]
[ 4.20972465]
[-4.46240652]]
第600次循环,误差率:-0.0005759586544488722
theta:
[[ 1.3325051 ]
[ 4.28526073]
[-4.54132712]]
第700次循环,误差率:-0.00052536761251323
theta:
[[ 1.35011952]
[ 4.35366801]
[-4.61278704]]
第800次循环,误差率:-0.000483137353995138
theta:
[[ 1.3661004 ]
[ 4.416195 ]
[-4.67809338]]
第900次循环,误差率:-0.0004473383117375322
theta:
[[ 1.38071708]
[ 4.47378699]
[-4.73823573]]
最终模型
[[ 1.3940483 ]
[ 4.52666202]
[-4.79344382]]
最后,本地会产生一个文件名为thetaModel的文件,其中保存的内容就是最终模型
'''
自此,逻辑回归算法,从读入数据到训练数据再到保存模型已经全部完成,之后只需要考虑使用该模型即可。
模型的使用
另建一个文件,用来使用训练出来的逻辑回归模型。
同目录下新建一个文件test.py,用来测试我们的模型
1.使用流程分析
首先对测试步骤进行分析
S
t
e
p
1
:
导
入
我
们
训
练
好
的
逻
辑
回
归
模
型
(
之
前
保
存
在
本
地
的
t
h
e
t
a
M
o
d
e
l
文
件
)
S
t
e
p
2
:
导
入
测
试
集
数
据
,
我
们
要
拟
合
的
数
据
S
t
e
p
3
:
按
照
回
归
模
型
对
数
据
进
行
预
测
S
t
e
p
4
:
保
存
最
终
预
测
结
果
存
入
本
地
\begin{aligned} & Step1:导入我们训练好的逻辑回归模型(之前保存在本地的thetaModel文件)\\ & Step2:导入测试集数据,我们要拟合的数据\\ & Step3:按照回归模型对数据进行预测\\ & Step4:保存最终预测结果存入本地\\ \end{aligned}
Step1:导入我们训练好的逻辑回归模型(之前保存在本地的thetaModel文件)Step2:导入测试集数据,我们要拟合的数据Step3:按照回归模型对数据进行预测Step4:保存最终预测结果存入本地
执行完毕后,我们就可以查看预测效果了
2.导入训练好的逻辑回归模型和要预测的数据
这一步只需要从文件中读入数据就好了。
这里可以先导入之前我们写好的训练模块,方便使用模块中的方法(预测函数,误差分析等)
先导入模型,再导入数据
import numpy as np
import LogisticRegressionTrain as LRT
def loadModel(fileName):
'''
loadModel()函数,根据文件名提取出模型theta,并返回一个n*1的矩阵
输入:fileName文件名
输出:一个(m*n)的矩阵,作为模型
一般来说,线性回归的theta模型是n*1的矩阵。
我们训练后保存的是一个1*n的列表,因此需要读出来后转置一下
'''
f=open(fileName)
theta=[]
for line in f.readlines():#遍历文件每一行(这里只有一行)
tmpt=[]
lines=line.strip().split("\t")#格式化后,用\t分割字符串
for x in lines:
tmpt.append(float(x))#将每一行的每一个字符串转为浮点数保存入列表中
theta.append(tmpt)#将该行的值
f.close()
return (np.mat(theta)).T
def loadData(fileName,n):
'''
loadData()函数,通过文件名获取文件的数据,最后输出一个m*n的矩阵,作为样本
输入:fileName文件名
输出:一个(m*n)的矩阵,作为样本
'''
f=open(fileName)
x=[]
for line in f.readlines():
tmpx=[]
lines=line.strip().split("\t")
if len(lines) != (n-1):#对于不合格的数据不要
continue
tmpx.append(1)#先加入一个常数项
for i in lines:#加入后续的特征值
tmpx.append(float(i))
x.append(tmpx)
f.close()
return np.mat(x)
if __name__=="__main__":
theta=loadModel("thetaModel")
print(theta)
n=np.shape(theta)[0]
print(loadData("testData",n))
'''模型使用前面保存的模型
[[ 1.3940483 ]
[ 4.52666202]
[-4.79344382]]
数据随机生成吧
'''
模型取出后,数据导入,接下来就是利用模型和数据进行预测了
3.预测结果
直接使用拟合函数h(x)计算即可,调用训练模块中的函数可以让我们省的重复写.
在上面的代码下面继续写
def predictY(x,theta):
'''
predictY()函数,计算获得预测值,保存在(m*1)的的矩阵中
输入:x是一个(m*n)的矩阵
theta是一个(n*1)的矩阵
输出:y判断矩阵
'''
y=LRT.forecastFunction(theta,x);#直接调用库求预测值
m=np.shape(y)[0]
for i in range(m):#遍历预测值,将其划为0,1
if y[i,0]<0.5:
y[i,0]=0
else:
y[i,0]=1
return y
#-------------补全测试样例------------
if __name__=="__main__":
theta=loadModel("thetaModel")
print(theta)
n=np.shape(theta)[0]
x=loadData("testData",n)
print(x)
y=predictY(x,theta)
print(y)
4.结果保存与分析
将预测结果保存至本地。
通过python的io流即可。
def saveResult(fileName,y):
'''
saveResult()函数,将结果y(m*1)的矩阵保留到文件fileName中
输入:fileName文件名
y(m*1)的矩阵
'''
f=open(fileName,"w")
m=np.shape(y)[0]
res=[]
for i in range(m):
res.append(str(y[i,0]))
f.write("\t".join(res))
f.close()
#-------------补全测试样例------------
if __name__=="__main__":
#读取训练好的模型
theta=loadModel("thetaModel")
print(theta)
#读取要预测的数据
n=np.shape(theta)[0]
x=loadData("testData",n)
print(x)
#数据预测
y=predictY(x,theta)
print(y)
#结果保存
saveResult("predictY",y)
'''
可以在本地中查看到输出文件,该文件就是我们预测的训练集结果
'''
自此,逻辑回归的实现已经基本完成
其实后续还有很多可以操作的空间,比如之前学到的模型评估和进一步优化问题:模型的效果评判及模型的进一步优化问题_永远鲜红の幼月的博客
- 将数据集分成训练集,交叉验证集,测试数据集。
- 精确度和召回率的计算
- 正则化
- 多项式的选择
- …
但是这些全都写在这一篇文章里显得过为臃肿了。
我们本篇文章的目标就是将之前的逻辑回归理论转化为代码实现。
现在这个目标已经基本完成了,剩下的就等以后再进行把。
总结
我们总共写了4个python文件
- LogisticRegressionTrain.py文件,用于训练逻辑回归θ模型。其中包含了:
- forecastFunction()函数作为拟合函数
- errorRate()函数作为代价计算
- loopGradientDescent()函数作为循环梯度下降的实现函数
- dataLoad()和saveModel()函数作为读取样本文件和保存θ模型。
- 生成了文件thetaModel用来保存训练好的θ模型
- test.py文件,用于测试我们的逻辑回归θ模型,其中包含了:
- loadModel(),loadData()和saveResult()三个函数分别用于读取训练好的θ模型,读取要预测的数据,和保存的预测结果
- predictY()函数作为预测函数,其实就i是调用了LogisticRegressionTrain.py中的拟合函数
- 生成了文件predictY用于保存预测的结果
- randomData.py文件,用于随机生成我们的训练数据,其中包含了:
- randomTrainData()函数,用来随机生成数据
- saveTrainData()函数,用来将我们生成的数据保存在本地
- 生成了文件trainData用于保存训练集
- 生成了文件testData用于保存测试数据集
- getPicture.py文件,用于直观的(散点图)查看我们生成的数据,其中包含了:
- drawPicture()函数,用来将点集画图
- loadData()函数,用来加载点集
共计4个python文件,4个数据文件
完整代码
在gitee中:分类算法-逻辑回归 · 15538382334/机器学习入门实践
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)