本博客的理论细节在这里:机器学习入门-分类问题的拟合

本博客侧重于实现,细节理论不再过多赘述,只简单介绍。

线性模型-二分类问题理论分析

线性回归是一个处理二分类问题的常见分类算法,其得出的回归函数为线性模型回归函数

二分类问题:给出一个参数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+bkxbkx线x1kbθ=[b,k]T,x=[1,x]Thθ(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)1kxb,: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(1hθ(x))if k=0J(θ)=Cost(hθ(x),y)=(log(hθ(x))y+log(1hθ(x))(1y))

从单样本推广到多样本:

极大似然估计法此处不详细说明
对 于 多 个 训 练 样 本 { 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=1mCost(hθ(x(i)),y(i))=m1i=1m[log(hθ(x(i)))y(i)+log(1hθ(x(i)))(1y(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αθjJ(θ)θj:=θjαθj(m1[i=1my(i)loghθ(x(i))+(1y(i))log(1hθ(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αθjJ(θ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/机器学习入门实践

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐