3.0代码(使用Ligistic Regression进行多分类)
题目:在本次练习中,你将使用逻辑回归和神经网络来识别手写数字(从0到9)。今天,自动手写数字识别被广泛使用,从识别信封上的邮政编码到识别银行支票上的金额。这个练习将向您展示如何将您所学的方法用于此分类任务。在第一部分中,将扩展以前的逻辑回归,并将其应用于one-vs-all分类。
关于数据:本次的数据是以.mat格式储存的,mat格式是matlab的数据存储格式,按照矩阵保存,与numpy数据格式兼容,适合于各种数学运算,因此这次主要使用numpy进行运算。ex3data1中有5000个训练样例,其中每个训练样例是一个20像素×20像素灰度图像的数字,每个像素由一个浮点数表示,该浮点数表示该位置的灰度强度。每个20×20像素的网格被展开成一个400维的向量。这些每个训练样例都变成数据矩阵X中的一行。这就得到了一个5000×400矩阵X,其中每一行都是手写数字图像的训练样例。训练集的第二部分是一个包含训练集标签的5000维向量y,“0”的数字标记为“10”,而“1”到“9”的数字按自然顺序标记为“1”到“9”。
数据集链接:https://pan.baidu.com/s/1cEgQIvehUcLxZ0WVhxcPuQ
提取码: xejn
import numpy as np
import scipy.io as scio
import matplotlib.pyplot as plt
# 根据输入的数据和参数,计算神经网络的输出
def predict_nn(X, theta1, theta2):
m = X.shape[0]
X = np.c_[np.ones(m), X]
# 隐藏层的输入
z1 = theta1.dot(X.T)
# 增加一行维度
z1 = np.row_stack((np.ones(z1.shape[1]), z1))
# 隐藏层的输出
A1 = sigmoid(z1)
# 输出层的输入
z2 = theta2.dot(A1)
# 输出层的输出
A2 = sigmoid(z2.T)
# 进行预测
P = np.zeros(m)
for num in range(m):
# 找到第num行中,与该行最大值相等的列的下标,此时下标的范围是[0,9]
# label的范围是[1,10],需要把下标的值+1
# np.where()返回的是一个长度为2的元祖,保存的是满足条件的下标
# 元组中第一个元素保存的是行下标,第二元素保存的是列下标
index = np.where(A2[num, :] == np.max(A2[num, :]))
P[num] = index[0][0].astype(int) + 1
return P
# 向量化logistic的预测函数
def predict_one_vs_all(X, all_theta):
m = X.shape[0]
X = np.c_[np.ones(m), X]
# 标签数10
num_labels = all_theta.shape[0]
# preds[m][k]是第m个样本属于k的概率
preds = sigmoid(X.dot(all_theta.T))
P = np.zeros(m)
for num in range(m):
# 找到第num行中,与该行最大值相等的列的下标,此时下标的范围是[0,9]
# label的范围是[1,10],需要把下标的值+1
# np.where()返回的是一个长度为2的元祖,保存的是满足条件的下标
# 元组中第一个元素保存的是行下标,第二元素保存的是列下标
index = np.where(preds[num, :] == np.max(preds[num, :]))
P[num] = index[0][0].astype(int) + 1
return P
def one_vs_all(X, Y, num_labels, lmd):
# 给数据添加偏置维度
X = np.c_[np.ones(X.shape[0]), X]
n = X.shape[1]
# 保存所有theta的集合
all_theta = np.zeros((num_labels, n))
# Y中的值是1~10
for i in range(1, num_labels + 1):
init_theta = np.zeros((n, 1));
y = (Y == i).astype(int)
def cost_func(t):
return lr_cost_function(X, y, t, lmd)[0]
def grad_func(t):
return lr_cost_function(X, y, t, lmd)[1]
theta, cost, *unused = opt.fmin_bfgs(f=cost_func, fprime=grad_func, x0=init_theta, maxiter=100,
full_output=True, disp=False)
all_theta[i - 1, :] = theta.T
return all_theta
# 这个函数是用来求代价和梯度,和前面ex2的costfunction相同
def lr_cost_function(X, Y, theta, lmd):
m = X.shape[0]
g = sigmoid(X.dot(theta))
# g.shape = [m,]
# Y.shape = [m,1]
# 两者相减得到的是[m,m]
Y = Y.reshape(Y.size)
cost = (-Y.T).dot(np.log(g)) - ((1 - Y).T).dot(np.log(1 - g))
cost = cost / (m) + lmd * (theta.T).dot(theta) / (2 * m)
grad = (X.T).dot(g - Y) / m
grad[0] = grad[0]
grad[1:] = grad[1:] + (lmd * theta[1:]) / m
return cost, grad
def display_data(x):
(m, n) = x.shape
# 设置每个小图例的宽度和高度
width = np.round(np.sqrt(n)).astype(int)
height = (n / width).astype(int)
# 设置图片的行数和列数
rows = np.floor(np.sqrt(m)).astype(int)
cols = np.ceil(m / rows).astype(int)
# 设置图例之间的间隔
pad = 1
# 初始化图像数据
display_array = -np.ones((pad + rows * (height + pad),
pad + cols * (width + pad)))
# 把数据按行和列复制进图像中
current_image = 0
for j in range(rows):
for i in range(cols):
if current_image > m:
break
# [:,np.newaxis]可以让指定的那一列数据以列的形式返回和指定
# 否则返回的是行的形式
max_val = np.max(np.abs(x[current_image, :]))
display_array[pad + j * (height + pad) + np.arange(height),
pad + i * (width + pad) + np.arange(width)[:, np.newaxis]] = \
x[current_image, :].reshape((height, width)) / max_val
current_image += 1
if current_image > m:
break
# 显示图像
plt.figure()
# 设置图像色彩为灰度值,指定图像坐标范围
plt.imshow(display_array, cmap='gray', extent=[-1, 1, -1, 1])
plt.axis('off')
plt.title('Random Seleted Digits')
# 使用Ligistic Regression进行多分类
# ============================== 1.读取数据和初始化 ============================
# 使用scipy.io中的函数读取mat文件,data的格式是字典
data = scio.loadmat('ex3data1.mat')
# 根据关键字,分别获得输入数据和输出的真值
# print(type(Y),type(X)) # X和Y都是numpy.narray格式,也就是数组格式
X = data['X']
Y = data['y']
print(X.shape)
print(Y.shape)
# 随机取出其中的100个样本,显示结果
m = X.shape[0]
# 从[0,m-1]之间,随机生成一个序列
rand_indices = np.random.permutation(range(m))
selected = X[rand_indices[1:100], :]
# 显示手写数字样例
display_data(selected)
# ========================= 2.向量化Logistic Rgression =========================
# 测试函数lr_cost_function的功能
"""
theta_t = np.array([-2, -1, 1, 2])
X_t = np.c_[np.ones(5), np.arange(1, 16).reshape((3, 5)).T/10]
y_t = np.array([1, 0, 1, 0, 1])
lmda_t = 3
cost,grad = lr_cost_function(X_t,y_t,theta_t,lmda_t)
np.set_printoptions(formatter={'float': '{: 0.6f}'.format})
print('Cost: {:0.7f}'.format(cost))
print('Expected cost: 3.734819')
print('Gradients:\n{}'.format(grad))
print('Expected gradients:\n[ 0.146561 -0.548558 0.724722 1.398003]')
"""
# 训练模型
lmd = 0.01
num_labels = 10
all_theta = one_vs_all(X, Y, num_labels, lmd)
# =============================== 3.预测 =======================================
pred = predict_one_vs_all(X, all_theta)
# 这里一定要把Y.shape变成(m,),否则Y.shape = (m,1),带入是无效的
Y = Y.reshape(Y.size)
print('Training set accurayc:{}'.format(np.mean(pred == Y) * 100))
使用简单的神经网络对“手写数字”进行训练和识别
import numpy as np
import scipy.io as scio
import matplotlib.pyplot as plt
# 根据输入的数据和参数,计算神经网络的输出
def predict_nn(X, theta1, theta2):
m = X.shape[0]
X = np.c_[np.ones(m), X]
# 隐藏层的输入
z1 = theta1.dot(X.T)
# 增加一行维度
z1 = np.row_stack((np.ones(z1.shape[1]), z1))
# 隐藏层的输出
A1 = sigmoid(z1)
# 输出层的输入
z2 = theta2.dot(A1)
# 输出层的输出
A2 = sigmoid(z2.T)
# 进行预测
P = np.zeros(m)
for num in range(m):
# 找到第num行中,与该行最大值相等的列的下标,此时下标的范围是[0,9]
# label的范围是[1,10],需要把下标的值+1
# np.where()返回的是一个长度为2的元祖,保存的是满足条件的下标
# 元组中第一个元素保存的是行下标,第二元素保存的是列下标
index = np.where(A2[num, :] == np.max(A2[num, :]))
P[num] = index[0][0].astype(int) + 1
return P
# 计算sigmoid函数值
def sigmoid(z):
g = 1 / (1 + np.exp(-z))
return g
# 向量化logistic的预测函数
def predict_one_vs_all(X, all_theta):
m = X.shape[0]
X = np.c_[np.ones(m), X]
# 标签数10
num_labels = all_theta.shape[0]
# preds[m][k]是第m个样本属于k的概率
preds = sigmoid(X.dot(all_theta.T))
P = np.zeros(m)
for num in range(m):
# 找到第num行中,与该行最大值相等的列的下标,此时下标的范围是[0,9]
# label的范围是[1,10],需要把下标的值+1
# np.where()返回的是一个长度为2的元祖,保存的是满足条件的下标
# 元组中第一个元素保存的是行下标,第二元素保存的是列下标
index = np.where(preds[num, :] == np.max(preds[num, :]))
P[num] = index[0][0].astype(int) + 1
return P
def one_vs_all(X, Y, num_labels, lmd):
# 给数据添加偏置维度
X = np.c_[np.ones(X.shape[0]), X]
n = X.shape[1]
# 保存所有theta的集合
all_theta = np.zeros((num_labels, n))
# Y中的值是1~10
for i in range(1, num_labels + 1):
init_theta = np.zeros((n, 1));
y = (Y == i).astype(int)
def cost_func(t):
return lr_cost_function(X, y, t, lmd)[0]
def grad_func(t):
return lr_cost_function(X, y, t, lmd)[1]
theta, cost, *unused = opt.fmin_bfgs(f=cost_func, fprime=grad_func, x0=init_theta, maxiter=100,
full_output=True, disp=False)
all_theta[i - 1, :] = theta.T
return all_theta
# 这个函数是用来求代价和梯度,和前面ex2的costfunction相同
def lr_cost_function(X, Y, theta, lmd):
m = X.shape[0]
g = sigmoid(X.dot(theta))
# g.shape = [m,]
# Y.shape = [m,1]
# 两者相减得到的是[m,m]
Y = Y.reshape(Y.size)
cost = (-Y.T).dot(np.log(g)) - ((1 - Y).T).dot(np.log(1 - g))
cost = cost / (m) + lmd * (theta.T).dot(theta) / (2 * m)
grad = (X.T).dot(g - Y) / m
grad[0] = grad[0]
grad[1:] = grad[1:] + (lmd * theta[1:]) / m
return cost, grad
def display_data(x):
(m, n) = x.shape
# 设置每个小图例的宽度和高度
width = np.round(np.sqrt(n)).astype(int)
height = (n / width).astype(int)
# 设置图片的行数和列数
rows = np.floor(np.sqrt(m)).astype(int)
cols = np.ceil(m / rows).astype(int)
# 设置图例之间的间隔
pad = 1
# 初始化图像数据
display_array = -np.ones((pad + rows * (height + pad),
pad + cols * (width + pad)))
# 把数据按行和列复制进图像中
current_image = 0
for j in range(rows):
for i in range(cols):
if current_image > m:
break
# [:,np.newaxis]可以让指定的那一列数据以列的形式返回和指定
# 否则返回的是行的形式
max_val = np.max(np.abs(x[current_image, :]))
display_array[pad + j * (height + pad) + np.arange(height),
pad + i * (width + pad) + np.arange(width)[:, np.newaxis]] = \
x[current_image, :].reshape((height, width)) / max_val
current_image += 1
if current_image > m:
break
# 显示图像
plt.figure()
# 设置图像色彩为灰度值,指定图像坐标范围
plt.imshow(display_array, cmap='gray', extent=[-1, 1, -1, 1])
plt.axis('off')
plt.title('Random Seleted Digits')
# 使用简单的神经网络对“手写数字”进行训练和识别
# ========================= 1.读取数据,显示随机样例 ===========================
data = scio.loadmat('ex3data1.mat')
X = data['X']
Y = data['y'].flatten()
# 随机取出其中的100个样本,显示结果
m = X.shape[0]
# 从[0,m-1]之间,随机生成一个序列
rand_indices = np.random.permutation(range(m))
selected = X[rand_indices[1:100], :]
# 显示手写数字样例
display_data(selected)
# plt.show()
# ======================= 2.读取神经网络的参数 =================================
weight = scio.loadmat('ex3weights.mat')
# theta1.shape=(25,401),隐藏层有25个节点,输入数据为401维(添加了1个维度的偏置)
# Theta2.shape=(10,26),10输出层有10个节点,隐藏层添加一个维度后,有25个输出
theta1 = weight['Theta1']
theta2 = weight['Theta2']
P = predict_nn(X, theta1, theta2)
print('Training set accuracy: {}'.format(np.mean(P == Y) * 100))
# ======================= 3.随机选取样本,并显示预测结果 =========================
rp = np.random.permutation(range(m))
for i in range(m):
print('Displaying Example image')
example = X[rp[i]]
example = example.reshape((1, example.size))
display_data(example)
pred = predict_nn(example, theta1, theta2, )
print('Neural network prediction: {} (digit {})'.format(pred, np.mod(pred, 10)))
plt.show()
s = input('Paused - press ENTER to continue, q + ENTER to exit: ')
if s == 'q':
break