EX3:逻辑回归实现手写数字识别(多类分类)
1.读取数据
注意给出的数据集是以mat形式为后缀,在python中可以使用scipy.io中的函数loadmat()读取mat文件。
import numpy as np
from scipy.io import loadmat
data=loadmat('ex3data1.mat')
读取数据以后看一下data长什么样子:
{‘header’: b’MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Sun Oct 16 13:09:09 2011’,
‘version’: ‘1.0’,
‘globals’: [],
‘X’: array([[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
…,
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.]]),
‘y’: array([[10],
[10],
[10],
…,
[ 9],
[ 9],
[ 9]], dtype=uint8)}
可以看到data里面分为‘X’和y两个部分,作为输入数据和标签,分别看看它们的大小:
所以数据集中包含5000个400维的特征向量,即每一个x都含有400个特征.
且y中含有1-10共10种标签。
2.Sigmoid函数
逻辑回归模型的假设函数中使用到Sigmoid函数,其公式为:
g
(
z
)
=
1
1
+
e
−
z
g(z)=\frac1{1+e^{-z}}
g(z)=1+e−z1
逻辑回归的假设函数为:
h
θ
(
x
)
=
1
1
+
e
−
θ
T
X
h_{\theta}(x)=\frac1{1+e^{-\theta^T X}}
hθ(x)=1+e−θTX1
def sigmoid(z):
return 1/(1+np.exp(-z))
3.代价函数
逻辑回归问题中的代价函数表述为:
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(\theta)=\frac1 m \sum\limits_{i=1}^m -(y^{(i)}log(h_{\theta}(x^{(i)}))+ (1-y)^{(i)}log(1-h_{\theta}(x^{(i)})))
J(θ)=m1i=1∑m−(y(i)log(hθ(x(i)))+(1−y)(i)log(1−hθ(x(i))))
def cost(theta,X,y,alpha):
m=X.shape[0]
hthetax=sigmoid(X.dot(theta))
return -alpha*(y.dot(np.log(hthetax))+(1-y).dot(np.log(1-hthetax)))/m
4.梯度
为了在拟合模型时使用Scipy
库中的优化函数,要定义一个函数计算代价函数的梯度,公式为:
∂
∂
θ
i
J
(
θ
)
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
(
i
)
\frac{\partial}{\partial \theta_i}J(\theta)= \frac1 m( h_{\theta}(x^{(i)})-y^{(i)})x^{(i)}
∂θi∂J(θ)=m1(hθ(x(i))−y(i))x(i)
写成矩阵形式:
h
θ
(
X
)
=
X
θ
,
s
i
z
e
:
(
5000
,
1
)
h
θ
(
X
)
−
y
=
X
θ
−
y
,
s
i
z
e
:
(
5000
,
1
)
返
回
值
s
i
z
e
=
(
400
,
1
)
∴
∂
∂
θ
i
J
(
θ
)
=
X
T
(
h
θ
(
X
)
−
y
)
/
m
h_{\theta}(X)=X\theta,size:(5000,1) \\ h_{\theta}(X)-y=X\theta-y,size:(5000,1) \\ 返回值size=(400,1) \\ \therefore \frac{\partial}{\partial \theta_i}J(\theta)= X^T(h_{\theta}(X)-y)/m
hθ(X)=Xθ,size:(5000,1)hθ(X)−y=Xθ−y,size:(5000,1)返回值size=(400,1)∴∂θi∂J(θ)=XT(hθ(X)−y)/m
根据上面推出的矩阵形式公式构建函数如下:
def grad(theta,X,y):
m=X.shape[0]
hthetax=sigmoid(X.dot(theta))
return (X.T.dot(hthetax-y))/m
5.分类器构建
这次我们要利用逻辑回归来做的不是单纯的二分类而是十分类,根据视频课程中所说,我们可以对每一个数字都做一次二分类。
例如数字1,我将把所有手写数字图片分为是1和不是1两类,从而习得一组针对于数字1的分类权重,以此类推,我们可以最终习得十组分类权重从而组成一个分类器。
分类器构建过程解析如下:
- 初始化最终权重矩阵
- 构造输入数据矩阵X(添加偏置项)
- 对于1-10这十个数字,都初始化一个权重向量,利用优化函数来寻找最佳权重
- 将每一个数字得到的权重向量放入权重矩阵中
from scipy.optimize import minimize
def ten_classification(X,y):
final_theta=np.zeros((10,X.shape[1]+1))#每一行都将是针对某一个数字的二分类权重向量(含偏置项)
#为X添加偏置项
ones_col=np.ones(X.shape[0])
X=np.c_[ones_col,X]
for i in range(1,11):
theta=np.zeros(X.shape[1])
#提取针对数字i的二分类器
y_i=np.array([1 if label==i else 0 for label in y])
#优化函数寻找theta
result=minimize(fun=cost,x0=theta,args=(X,y_i),method='TNC',jac=grad)
final_theta[i-1,:]=result.x
return final_theta
为保证运算过程中矩阵维度的准确性,接下来分析一下分类器中各个量的大小:
final_theta.shape
:(10,401)X.shape
:(5000,401)theta.shape
:(401,)y.shape
:(5000,)
6.分类器结果输出
调用分类器输出结果:
final_theta=ten_classification(data['X'],data['y'])
运行以上代码出现如下Warning,暂时还未解决:
- RuntimeWarning: divide by zero encountered in log
return -(y.dot(np.log(hthetax))+(1-y).dot(np.log(1-hthetax)))/m- RuntimeWarning: overflow encountered in exp
return 1/(1+np.exp(-z))
7.分类结果评估
将得到的权重模型再次用于数据集X,将计算得到的结果和标签进行比较并得出准确率:
def predict_all(X,final_theta):
#添加偏置
ones_col=np.ones(X.shape[0])
X=np.c_[ones_col,X]
pre_max=np.argmax(sigmoid(X.dot(final_theta.T)),axis=1)#找出hthetax中最大的一个,为预测值-1
return pre_max+1
y_pre=predict_all(data['X'],final_theta)
correct=np.array([1 if y_pre[i]==data['y'][i] else 0 for i in range(len(y_pre))])
accuracy=(correct.sum())/len(correct)
print('Accuracy:{}%'.format(accuracy*100))
输出为:Accuracy:97.42%