1 多分类
这个部分需要你实现手写数字(0到9)的识别。你需要扩展之前的逻辑回归,并将其应用于一对多的分类。
1.1 数据集
这是一个MATLAB格式的.m文件,其中包含5000个20*20像素的手写字体图像,以及他对应的数字。另外,数字0的y值,对应的是10
用Python读取我们需要使用SciPy
1.2 数据可视化
随机展示100个数据
1.3 将逻辑回归向量化
你将用多分类逻辑回归做一个分类器。因为现在有10个数字类别,所以你需要训练10个不同的逻辑回归分类器。为了让训练效率更高,将逻辑回归向量化是非常重要的,不要用循环。
1.3.1 向量化代价函数
sigmoid 函数
g 代表一个常用的逻辑函数(logistic function)为S形函数(Sigmoid function),公式为:
合起来,我们得到逻辑回归模型的假设函数:
即:g(Θ.T * x) = hΘ(x)
代价函数:
1.3.2 向量化梯度
没正则化的时候,逻辑回归的代价是一个向量,第j个元素定义如下:
想要正则化它,我们先把他每一行都写出来
其中
记住,x(i)是一个向量,(hθ(x(i))-y(i))但是一个数字
要理解前面式子的最后一步,我们令βi=(hθ(x(i)) - y(i))。于是我们可以得出如下等式
1.3.3 向量化正则化逻辑回归
梯度更新公式如下:
向量化后的梯度更新公式如下:
1.4 一对多分类器
现在我们已经定义了代价函数和梯度函数,现在是构建分类器的时候了。
对于这个任务,我们有10个可能的类,并且由于逻辑回归只能一次在2个类之间进行分类,我们需要多类分类的策略。
在本练习中,我们的任务是实现一对一全分类方法,其中具有k个不同类的标签就有k个分类器,每个分类器在“类别 i”和“不是 i”之间决定。
我们将把分类器训练包含在一个函数中,该函数计算10个分类器中的每个分类器的最终权重,并将权重返回为k*(n + 1)数组,其中n是参数数量。
1.4.1 一对多预测
我们现在准备好最后一步 - 使用训练完毕的分类器预测每个图像的标签。 对于这一步,我们将计算每个类的类概率,对于每个训练样本(使用当然的向量化代码),并将输出类标签为具有最高概率的类。
2 神经网络
在前面一个部分,我们已经实现了多分类逻辑回归来识别手写数字。但是,逻辑回归并不能承载更复杂的假设,因为他就是个线性分类器。
这部分,你需要实现一个可以识别手写数字的神经网络。神经网络可以表示一些非线性复杂的模型。权重已经预先训练好,你的目标是在现有权重基础上,实现前馈神经网络。
2.1 模型表达
现有模型图示如下
输入是图片的像素值,20*20像素的图片有400个输入层单元,不包括需要额外添加的加上常数项。
材料已经提供了训练好的神经网络的参数θ(1),θ(2),有25个隐层单元和10个输出单元(10个输出)
2.2 前馈神经网络和预测
你需要实现前馈神经网络预测手写数字的功能。和之前的一对多分类一样,神经网络的预测会把(hθ(x))k中值最大的,作为预测输出。
代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
from scipy.io import loadmat
from scipy.optimize import minimize
from sklearn.metrics import classification_report#这个包是评价报告
# sigmoid函数
def sigmoid(z):
return 1 / (1 + np.exp(-z))
# 代价函数J(θ)
def cost(theta, X, y, learningRate):
theta = np.asmatrix(theta)
X = np.asmatrix(X)
y = np.asmatrix(y)
first = np.multiply(-y, np.log(sigmoid(X * theta.T))) # sigmoid(X * theta.T) = g(X * theta.T)
second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T))) # 偏置值
reg = (learningRate / (2 * len(X))) * np.sum(np.power(theta[:, 1:theta.shape[1]], 2)) # len(X) = m,θ0不惩罚
return np.sum(first - second) / len(X) + reg
# 向量化梯度函数
def gradient(theta, X, y, learningRate):
theta = np.asmatrix(theta)
X = np.asmatrix(X)
y = np.asmatrix(y)
parameters = int(theta.ravel().shape[1]) # .ravel()将矩阵降维1维
error = sigmoid(X * theta.T) - y
grad = ((X.T * error) / len(X)).T + ((learningRate / len(X)) * theta) # (1/m)*X.T*(hθ(x)-y) + (λ/m)*θj
# 截止这里梯度还不是正则化的
grad[0, 0] = np.sum(np.multiply(error, X[:, 0])) / len(X)
return np.array(grad).ravel()
# 一对多分类器:我们将把分类器训练包含在一个函数中,该函数计算10个分类器中的每个分类器的最终权重,并将权重返回为k*(n + 1)数组,其中n是参数数量。
def one_vs_all(X, y, num_labels, learning_rate):
rows = X.shape[0]
params = X.shape[1]
# k个分类器的每个参数的权重数组k*(n + 1)数组
all_theta = np.zeros((num_labels, params + 1))
# 在最开始之前插入一行都是1,用于计算偏置值
X = np.insert(X, 0, values=np.ones(rows), axis=1)
# 标签是1开始而不是0开始,训练每一个分类器
for i in range(1, num_labels + 1):
theta = np.zeros(params + 1)
y_i = np.array([1 if label == i else 0 for label in y])
y_i = np.reshape(y_i, (rows, 1))
# fun: 求最小值的目标函数
# x0:变量的初始猜测值,如果有多个变量,需要给每个变量一个初始猜测值。
# args:常数值,后面demo会讲解,fun中没有数字,都以变量的形式表示,对于常数项,需要在这里给值
# method:求极值的方法,官方文档给了很多种。一般使用默认。每种方法我理解是计算误差,反向传播的方式不同而已,这块有很大理论研究空间
# constraints:约束条件,针对fun中为参数的部分进行约束限制
# jac:梯度矢量的计算方法
# res:优化结果.优化结果表示为OptimizeResult Object 。
# 重要属性是:x解决方案数组,success布尔值标志,指示优化程序是否成功退出,以及message描述终止的原因。
# 看到OptimizeResult用于其他属性的描述。
fmin = minimize(fun=cost, x0=theta, args=(X, y_i, learning_rate), method='TNC', jac=gradient)
all_theta[i - 1, :] = fmin.x
return all_theta
def predict_all(X, all_theta):
rows = X.shape[0]
params = X.shape[1]
num_labels = all_theta.shape[0]
# 和以前一样,插入匹配形状
X = np.insert(X, 0, values=np.ones(rows), axis=1)
# 转换为矩阵
X = np.asmatrix(X)
all_theta = np.asmatrix(all_theta)
# 计算每个训练实例上每个类的类概率
h = sigmoid(X * all_theta.T)
# 以最大概率创建索引数组
# axis = 0:所有的数组都要进行比较,只是比较的是这些数组相同位置上的数
# axis = 1:只会比较每个数组内的数的大小,结果也会根据有几个数组,产生几个结果
h_argmax = np.argmax(h, axis=1) # 用于返回一个numpy数组中最大值的索引值
# 因为我们的数组是零索引的,所以我们需要为真正的标签预测添加一个索引
h_argmax = h_argmax + 1
return h_argmax
if __name__ == '__main__':
# 多分类
data = loadmat('./ex3data1.mat')
data['X'].shape, data['y'].shape # (5000, 400) (5000, 1) 二维数组,5000行,400列
# 随机展示100个数据
sample_idx = np.random.choice(np.arange(data['X'].shape[0]), 100) # 从500种随机抽取100个数组成一维数组
sample_images = data['X'][sample_idx, :] # 取所有列的第sample_idx行,这些行的数据组成数组 (100,400)
fig, ax_array = plt.subplots(nrows=10, ncols=10, sharey=True, sharex=True, figsize=(12, 12)) # 函数返回一个figure图像和子图ax的array列表。x,y轴所有的子图共享
for r in range(10):
for c in range(10):
# 取sample_images第10 * r + c行的数据组成(20,20)的数组,将其转置
ax_array[r, c].matshow(np.array(sample_images[10 * r + c].reshape((20, 20))).T, cmap=matplotlib.cm.binary) # 把矩阵或数组绘制成图像,cmap将图片转为二进制灰度图像
plt.xticks(np.array([])) # 把坐标轴变成自己想要的样子
plt.yticks(np.array([])) # 将x,y轴的刻度标签等变为空
# plt.show() # 显示100个数据
rows = data['X'].shape[0] # 500
params = data['X'].shape[1] # 400
all_theta = np.zeros((10, params + 1)) # 二维数组
X = np.insert(data['X'], 0, values=np.ones(rows), axis=1) # arr原始数组,可一可多,obj插入元素位置,values是插入内容,axis是按行按列插入
theta = np.zeros(params + 1)
y_0 = np.array([1 if label == 0 else 0 for label in data['y']]) # [0 0 0 ... 0 0 0]
y_0 = np.reshape(y_0, (rows, 1)) # [[0] [0] [0] ... [0] [0] [0]]
# print(X.shape, y_0.shape, theta.shape, all_theta.shape) #(5000, 401) (5000, 1) (401,) (10, 401)
# print(np.unique(data['y'])) # 看下有几类标签,np.unique()函数是去除数组中的重复数字,并进行排序之后输出。
all_theta = one_vs_all(data['X'], data['y'], 10, 1) # 10个训练好的分类器
y_pred = predict_all(data['X'], all_theta)
print(classification_report(data['y'], y_pred)) #报告中显示每个类的精确度,召回率,F1值等信息。
# 神经网络
weight = loadmat("./ex3weights.mat")
theta1, theta2 = weight['Theta1'], weight['Theta2']
theta1.shape, theta2.shape # (25,401), (10,26)
# 插入常数项
X2 = np.asmatrix(np.insert(data['X'], 0, values=np.ones(X.shape[0]), axis=1))
y2 = np.asmatrix(data['y'])
X2.shape, y2.shape # (5000, 401), (5000, 1)
a1 = X2
z2 = a1 * theta1.T
z2.shape # (5000, 25)
a2 = sigmoid(z2)
a2.shape # (5000, 25)
a2 = np.insert(a2, 0, values=np.ones(a2.shape[0]), axis=1)
z3 = a2 * theta2.T
z3.shape # (5000, 10)
a3 = sigmoid(z3)
a3 # matrix([[1.12661530e-04, 1.74127856e-03, 2.52696959e-03, ...,
# 4.01468105e-04, 6.48072305e-03, 9.95734012e-01],
#[4.79026796e-04, 2.41495958e-03, 3.44755685e-03, ...,
# 2.39107046e-03, 1.97025086e-03, 9.95696931e-01],
#[8.85702310e-05, 3.24266731e-03, 2.55419797e-02, ...,
# 6.22892325e-02, 5.49803551e-03, 9.28008397e-01],
#...,
#[5.17641791e-02, 3.81715020e-03, 2.96297510e-02, ...,
# 2.15667361e-03, 6.49826950e-01, 2.42384687e-05],
#[8.30631310e-04, 6.22003774e-04, 3.14518512e-04, ...,
#1.19366192e-02, 9.71410499e-01, 2.06173648e-04],
#[4.81465717e-05, 4.58821829e-04, 2.15146201e-05, ...,
# 5.73434571e-03, 6.96288990e-01, 8.18576980e-02]])
y_pred2 = np.argmax(a3, axis=1) + 1
y_pred2.shape # (5000, 1)
print(classification_report(y2, y_pred))