【机器学习】多类别逻辑回归、神经网络ex3

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))。于是我们可以得出如下等式

Image Name

是的向量,与一开始等式左边的向量对应,后为,的列向量是,二者相乘之后是

1.3.3 向量化正则化逻辑回归

梯度更新公式如下:
在这里插入图片描述

向量化后的梯度更新公式如下:
在这里插入图片描述

1.4 一对多分类器

现在我们已经定义了代价函数和梯度函数,现在是构建分类器的时候了。
对于这个任务,我们有10个可能的类,并且由于逻辑回归只能一次在2个类之间进行分类,我们需要多类分类的策略。
在本练习中,我们的任务是实现一对一全分类方法,其中具有k个不同类的标签就有k个分类器,每个分类器在“类别 i”和“不是 i”之间决定。
我们将把分类器训练包含在一个函数中,该函数计算10个分类器中的每个分类器的最终权重,并将权重返回为k*(n + 1)数组,其中n是参数数量。

1.4.1 一对多预测

我们现在准备好最后一步 - 使用训练完毕的分类器预测每个图像的标签。 对于这一步,我们将计算每个类的类概率,对于每个训练样本(使用当然的向量化代码),并将输出类标签为具有最高概率的类。

2 神经网络

在前面一个部分,我们已经实现了多分类逻辑回归来识别手写数字。但是,逻辑回归并不能承载更复杂的假设,因为他就是个线性分类器。
这部分,你需要实现一个可以识别手写数字的神经网络。神经网络可以表示一些非线性复杂的模型。权重已经预先训练好,你的目标是在现有权重基础上,实现前馈神经网络。

2.1 模型表达

现有模型图示如下

Image Name
输入是图片的像素值,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))
吴恩达的机器学习课程中的ex3任务是关于手写数字识别的。在这个任务中,我们使用了一个包含5000个手写数字训练示例的数据集(ex3data1.mat)。每个训练示例都是一个20×20像素的灰度图像,被展开成了一个400维的向量。这些训练示例被存储在数据矩阵X中,其中每一行代表一个手写数字图像的训练示例。 此外,训练集的标签被存储在一个5000维的向量y中。为了与Octave/MATLAB索引兼容,我们把数字零映射为值10,并将数字1至9按其自然顺序标记为1至9。 在导入数据并初始化之后,我们可以开始使用这个数据集进行手写数字识别的任务了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【吴恩达】机器学习作业 ex3data1 -- 多分类逻辑回归Python)](https://blog.csdn.net/calmdownn/article/details/125992325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [吴恩达机器学习笔记---ex3(python实现)](https://blog.csdn.net/qq_45604750/article/details/107628153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值