吴恩达机器学习ex3 python实现(详细注释)

文章目录

  • 1、多分类
    • 1.1 数据集
    • 1.2 数据可视化
    • 1.3 矢量化 Logistic 回归
      • 1.3.1 向量化成本函数
      • 1.3.2 矢量化梯度
    • 1.4 一对多分类
  • 2.神经网络

1、多分类

在本练习中,您将使用逻辑回归和神经网络来识别手写数字(从 0 到 9)。 自动手写数字识别如今已得到广泛应用,从识别邮件信封上的邮政编码(邮政编码)到识别银行支票上的金额。 本练习将向您展示如何将您学到的方法用于此分类任务。
在练习的第一部分中,您将扩展之前的逻辑回归实现并将其应用于一对多分类。

1.1 数据集

import numpy as np  # NumPy 用于数值操作
import pandas as pd  # Pandas 用于数据处理
import matplotlib.pyplot as plt  # Matplotlib 用于绘图
from scipy.io import loadmat  # SciPy 用于加载 MATLAB 文件

ex3data1.mat 中提供了一个数据集,其中包含 5000 个手写数字的训练示例。 .mat 格式意味着数据已以本机 Octave/MATLAB 矩阵格式保存,而不是文本 (ASCII) 格式类似于 csv 文件。 可以使用 load 命令将这些矩阵直接读入您的程序。 加载后,正确尺寸和值的矩阵将出现在程序的内存中。 该矩阵已被命名,因此您无需为其指定名称。
在这里插入图片描述

def load_data(path):
    data = loadmat(path)
    X = data['X']
    y = data['y']
    return X,y
    # 这段代码定义了一个名为 load_data 的函数,该函数接受一个参数 path,表示文件路径。
	# 函数的功能是从指定的路径加载数据。首先,它使用 loadmat 函数从指定的路径加载数据文件。
	# 然后,它从加载的数据中提取出两个变量 X 和 y,分别代表输入特征和对应的标签。
	# 最后,函数返回 X 和 y,即输入特征和标签,供后续的数据处理和分析使用。
X, y = load_data('ex3data1.mat')
print(np.unique(y))  # 看下有几类标签
# [ 1  2  3  4  5  6  7  8  9 10]
X.shape, y.shape
# ((5000, 400), (5000, 1))

ex3data1.mat中有5000个训练样本,其中每个训练
示例是数字的 20 像素 x 20 像素灰度图像。 每个像素都由一个浮点数表示,指示该位置的灰度强度。 20 x 20 像素网格被“展开”为 400 维向量。 这些训练示例中的每一个都成为数据矩阵 X 中的一行。这为我们提供了一个 5000 x 400 矩阵 X,其中每一行都是手写数字图像的训练示例。
在这里插入图片描述
训练集的第二部分是一个 5000 维向量 y,其中包含训练集的标签。 为了使事情与没有零索引的 Octave/MATLAB 索引更加兼容,我们将数字零映射到值十。 因此,数字“0”被标记为“10”,而数字“1”到“9”则按照其自然顺序被标记为“1”到“9”。
第一个任务是将我们的逻辑回归实现修改为完全向量化(即没有“for”循环)。这是因为向量化代码除了简洁外,还能够利用线性代数优化,并且通常比迭代代码快得多。

1.2 数据可视化

您将首先可视化训练集的子集。 在 ex3.m 的第 1 部分中,代码从 X 中随机选择 100 行,并将这些行传递给 displayData 函数。 此函数将每行映射到 20 像素 x 20 像素的灰度图像,并将图像一起显示。 我们提供了 displayData 函数,我们鼓励您检查代码以了解它是如何工作的。 运行此步骤后,您应该会看到如图 1 所示的图像。
在这里插入图片描述

def plot_100_image(X):
    """
    随机打印100个数字
    参数 X: 包含数字图像的数据
    """
    sample_idx = np.random.choice(np.arange(X.shape[0]), 100)
    sample_images = X[sample_idx, :]  # 获取随机选择的数字图像数据
    # 创建一个包含10行10列的图形窗口,每个子图共享 x 轴和 y 轴,图形大小为8x8
    fig, ax_array = plt.subplots(nrows=10, ncols=10, sharey=True, sharex=True, figsize=(8, 8)) 
    # 遍历每个子图
    for row in range(10):
        for column in range(10):
             # 在当前子图中绘制对应索引的数字图像,并将其 reshape 为20x20的矩阵,使用灰度色彩显示
            ax_array[row, column].matshow(sample_images[10 * row + column].reshape((20, 20)),
                                   cmap='gray_r')   
    #具体解释:sample_images[10 * row + column] 表示在第 10 * row + column 行中的样本。然后,使用 .reshape((20, 20)) 将这个一维向量重新塑造为一个 20x20 的二维矩阵,这样可以将其作为图像进行显示。
    plt.xticks([])   # 去除 x 轴的刻度,使图像更美观
    plt.yticks([])   # 去除 y 轴的刻度,使图像更美观
    plt.show() # 显示图形

在这里插入图片描述

1.3 矢量化 Logistic 回归

您将使用多个一对多逻辑回归模型来构建多类分类器。 由于有 10 个类,因此您需要训练 10 个单独的逻辑回归分类器。 为了提高训练效率,确保您的代码得到良好的矢量化非常重要。 在本节中,您将实现逻辑回归的矢量化版本,该版本不使用任何 for 循环。 您可以使用上一个练习中的代码作为本练习的起点。

1.3.1 向量化成本函数

我们将首先编写代价函数的矢量化版本。 回想一下,逻辑回归中,代价函数是
在这里插入图片描述

为了计算求和中的每个元素,我们必须计算
在这里插入图片描述
事实证明,我们可以通过使用矩阵乘法来快速计算所有示例。 让我们将 X 和 θ 定义为
在这里插入图片描述
然后,通过计算矩阵乘积 Xθ,我们有
在这里插入图片描述
在最后一个等式中,我们用到了一个定理,如果 a 和 b 都是向量,aTb=bTa,这样我们就可以用一行代码计算出所有的样本。
定义代价函数:

def sigmoid(z):
    return 1 / (1 + np.exp(-z))     
def regularized_cost(theta, X, y, l):
    thetaReg = theta[1:]
    first = (-y*np.log(sigmoid(X@theta))) + (y-1)*np.log(1-sigmoid(X@theta))
    reg = (thetaReg@thetaReg)*l / (2*len(X))
    return np.mean(first) + reg

1.3.2 矢量化梯度

回想一下,(非正则化)逻辑回归成本的梯度是一个向量,其中第 j 个元素定义为
在这里插入图片描述
为了在数据集上向量化此操作,我们首先明确地写出所有 θ 的所有偏导数
在这里插入图片描述
观察到
在这里插入图片描述
在这里插入图片描述
梯度表示如下:

def regularized_gradient(theta, X, y, l):
    thetaReg = theta[1:]
    first = (1 / len(X)) * X.T @ (sigmoid(X @ theta) - y)
    reg = np.concatenate([np.array([0]),(l/len(X)) * thetaReg])
    return first + reg
    # np.concatenate() 是 NumPy 库中的一个函数,用于沿指定轴连接多个数组或矩阵。
    # 具体来说,它将多个数组按照给定的轴连接起来,生成一个新的数组。

1.4 一对多分类

在练习的这一部分中,您将通过训练多个正则化逻辑回归分类器来实现一对多分类,每个分类器对应数据集中的 K 个类别(图 1)。 在手写数字数据集中,K = 10,但您的代码应该适用于任何 K 值。
您现在应该完成一对多中的代码,为每个类别训练一个分类器。 特别是,您的代码应返回矩阵 θ ∈ R K×(N+1) 中的所有分类器参数,其中 θ 的每一行对应于一个类的学习逻辑回归参数。 您可以使用从 1 到 K 的“for”循环来完成此操作,独立训练每个分类器。

from scipy.optimize import minimize #导入 minimize 函数,它用于最小化(优化)一个目标函数。
def one_vs_all(X, y, l, K):
    '''
    定义了一个名为 one_vs_all 的函数,实现了通用的逻辑回归算法。它有四个参数:
    X:特征矩阵,形状为 (m, n+1),其中 m 是样本数量,n 是特征数量。这里的特征矩阵包含了截距项,即第一列全为 1。
    y:目标向量,形状为 (m, ),包含了每个样本的分类标签。
    l:正则化参数λ,用于控制正则化的强度。
    K:类别数量,即要进行分类的标签数目。
    '''
    all_theta = np.zeros((K, X.shape[1])) #初始化参数矩阵 all_theta,形状为 (K, n+1),其中 K 是类别数量,n+1 是特征数量(包括截距项)。初始值为全零。
    '''
    对每个类别循环进行一次逻辑回归训练。
    初始化参数向量 theta,形状与特征矩阵的列数相同。
    将目标向量 y 中的标签转换为二元分类问题。对于当前类别 i,将所有等于 i 的标签设为 1,其余标签设为 0。
    '''
    for i in range(1, K+1):
        theta = np.zeros(X.shape[1])
        y_i = np.array([1 if label == i else 0 for label in y])
        '''
        使用 minimize 函数最小化正则化的成本函数,从而求解当前类别的最优参数 theta。
        fun=regularized_cost 指定成本函数为 regularized_cost,x0=theta 指定初始参数向量为 theta。
        args=(X, y_i, l) 将特征矩阵 X、目标向量 y_i 和正则化参数 l 作为额外参数传递给成本函数。
        method='TNC' 指定优化算法为 TNC 算法(Truncated Newton Conjugate-Gradient)。
        jac=regularized_gradient 指定梯度函数为 regularized_gradient,用于计算成本函数的梯度。
        options={'disp': True} 设置优化选项,其中 'disp': True 表示在优化过程中打印迭代信息。
        将优化得到的最优参数 ret.x 存储在参数矩阵 all_theta 的对应行中。
        '''
        ret = minimize(fun=regularized_cost, x0=theta, args=(X, y_i, l), method='TNC', jac=regularized_gradient, options={'disp': True})
        all_theta[i-1,:] = ret.x
    return all_theta

训练完一对多分类器后,您现在可以使用它来预测给定图像中包含的数字。 对于每个输入,您应该使用经过训练的逻辑回归分类器计算它属于每个类的“概率”。 您的一对多预测函数将选择相应逻辑回归分类器输出最高概率的类,并返回类标签(1、2、…或 K)作为输入示例的预测。

def predict_all(X, all_theta):
    # 计算每个训练实例的每个类别的类别概率
    h = sigmoid(X @ all_theta.T)  # 注意的这里的all_theta需要转置
    # 创建具有最大概率索引的数组
    # 返回沿着指定轴的最大值的索引。
    h_argmax = np.argmax(h, axis=1)
    # 因为我们的数组是从零开始的,所以我们需要为真实标签预测添加1
    h_argmax = h_argmax + 1
    
    return h_argmax

raw_X, raw_y = load_data('E:\python work\machine learning\ML-homework\ex3-neural network\ex3data1.mat') #调用 load_data 函数加载数据集。
#ex3data1.mat 文件中包含了原始特征矩阵 raw_X 和标签向量 raw_y。
'''
在原始特征矩阵 raw_X 中插入一列全为1的偏置项,以便进行逻辑回归。这是为了对应模型中的截距项(intercept term)。
np.insert() 函数用于在数组中插入值,第一个参数是原始特征矩阵 raw_X,第二个参数是要插入的位置,这里是0,表示在第一列插入。
第三个参数是要插入的值,这里是1,表示偏置项。axis=1 表示在列方向插入。
最终得到的特征矩阵 X 的形状为 (5000, 401),其中5000是样本数量,401是特征数量(包括偏置项)。
'''
X = np.insert(raw_X, 0, 1, axis=1) # (5000, 401)
y = raw_y.flatten()  # 这里消除了一个维度,方便后面的计算 or .reshape(-1) (5000,)
'''
调用 one_vs_all 函数训练一个一对所有(One-vs-All)逻辑回归模型,用于多类别分类任务。
参数 X 是特征矩阵,y 是标签向量,1 是正则化参数 λ,10 是类别数量。
函数返回每个类别的训练参数,存储在参数矩阵 all_theta 中。
'''
all_theta = one_vs_all(X, y, 1, 10)
all_theta  # 每一行是一个分类器的一组参数
'''
使用训练好的参数 all_theta 对训练集特征矩阵 X 进行预测,得到预测的类别标签向量 y_pred。
predict_all 函数根据输入特征矩阵 X 和参数矩阵 all_theta 进行预测,并返回预测的类别标签。
'''
y_pred = predict_all(X, all_theta)
'''
计算预测的准确率。准确率定义为正确预测的样本数量占总样本数量的比例。
使用 NumPy 的 mean() 函数计算预测结果 y_pred 与真实标签 y 相等的比例,
因为当预测值等于真实值时,对应的值为 True,否则为 False。然后求平均值,即准确率。
'''
accuracy = np.mean(y_pred == y)
print ('accuracy = {0}%'.format(accuracy * 100))

结果如下:
在这里插入图片描述

2.神经网络

在本练习的前一部分中,您实现了多类逻辑回归来识别手写数字。 然而,逻辑回归无法形成更复杂的假设,因为它只是一个线性分类器。3 在练习的这一部分中,您将使用与之前相同的训练集实现一个神经网络来识别手写数字。 神经网络将能够表示形成非线性假设的复杂模型。 本周,您将使用我们已经训练过的神经网络中的参数。 您的目标是实现前馈传播算法以使用我们的权重进行预测。 在下周的练习中,您将编写用于学习神经网络参数的反向传播算法。提供的脚本 ex3.m 将帮助您逐步完成此练习。
在这里插入图片描述

'''
load_weight 是一个函数,用于加载神经网络的权重参数。
loadmat(path) 函数从指定路径加载数据,假设数据是以Matlab格式保存的文件。
加载的数据被赋值给变量 data。
data['Theta1'] 返回加载的数据中的键名为 'Theta1' 的值,即神经网络的第一层到第二层之间的权重参数矩阵。
data['Theta2'] 返回加载的数据中的键名为 'Theta2' 的值,即神经网络的第二层到输出层之间的权重参数矩阵。
函数最终返回两个参数,分别是神经网络的两个权重参数矩阵 Theta1 和 Theta2。
'''
def load_weight(path):
    data = loadmat(path)
    return data['Theta1'], data['Theta2']
theta1, theta2 = load_weight('ex3weights.mat')
theta1.shape, theta2.shape

# 使用 load_data 函数从指定路径加载数据集。假设数据集是以Matlab格式保存的文件。
# 加载的数据集包含特征矩阵 X 和标签向量 y。
X, y = load_data('ex3data1.mat')
# 将标签向量 y 进行扁平化操作,将多维数组降为一维数组。这样做是为了确保标签向量的形状与后续计算的一致性。
y = y.flatten()
# 在特征矩阵 X 中插入一列全为1的偏置项,以便进行神经网络训练。这是为了对应模型中的截距项(intercept term)。
# 使用 NumPy 的 insert() 函数在 X 的第一列(索引为0)插入值为1的偏置项。参数 axis=1 表示在列方向插入。
# 最终得到的特征矩阵 X 的形状为 (m, n+1),其中 m 是样本数量,n 是特征数量(不包括偏置项)。
X = np.insert(X, 0, values=np.ones(X.shape[0]), axis=1)  # intercept
# 打印输出特征矩阵 X 和标签向量 y 的形状,以确保数据预处理的正确性。
X.shape, y.shape
a1 = X #将输入特征矩阵 X 赋值给变量 a1,表示神经网络的第一层的激活值(也即是输入层的值)。
#计算神经网络的第二层输入值 z2,即第二层(隐藏层)的激活前的值。这一层的输入值是由第一层的激活值 a1 和第一层到第二层之间的权重参数矩阵 theta1 计算得到的。
# @ 表示矩阵乘法,theta1.T 是参数矩阵 theta1 的转置,确保参数矩阵的列数与特征矩阵 a1 的行数相匹配。这样,z2 的每一行对应一个样本,每一列对应一个隐藏单元。
z2 = a1 @ theta1.T 
# 打印输出隐藏层输入值 z2 的形状,以便检查计算结果的正确性。z2 的形状应该是 (m, hidden_units),其中 m 是样本数量,hidden_units 是隐藏层单元的数量。
z2.shape

# 在隐藏层的输入值矩阵z2中插入一列全为1的偏置项,以便进行神经网络的计算。这是为了对应模型中的截距项。
z2 = np.insert(z2, 0, 1, axis=1) 
# 计算隐藏层的激活值a2,使用 S 型函数(sigmoid 函数)对隐藏层的输入值进行激活。
a2 = sigmoid(z2)
a2.shape
z3 = a2 @ theta2.T
z3.shape
a3 = sigmoid(z3)
a3.shape
'''
算预测类别的向量 y_pred。由于输出层的激活值a3是对每个类别的预测概率,我们选择具有最高预测概率的类别作为最终预测结果。
np.argmax(a3, axis=1) 返回每个样本中预测概率最高的类别的索引,axis=1 表示沿着每行的方向进行操作,即对每个样本找到概率最高的类别。
由于索引是从0开始的,而类别是从1开始的,因此需要加1。
'''
y_pred = np.argmax(a3, axis=1) + 1 
accuracy = np.mean(y_pred == y)
print ('accuracy = {0}%'.format(accuracy * 100))  # accuracy = 97.52%

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/533547.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

马云支持阿里改革,预见AI时代的到来将带来巨大的变化

🦉 AI新闻 🚀 马云支持阿里改革,预见AI时代的到来将带来巨大的变化 摘要:马云在阿里内网发表《致改革 致创新》帖子,这是其退休五年来首次深度分享对阿里改革创新及未来的看法。文章中,马云对蔡崇信和吴泳…

微服务-4 Nacos

目录 一、注册中心 二、配置管理 1. 添加配置 2. 配置自动刷新 3. 多环境配置共享​编辑 一、注册中心 服务列表: 服务详情: 二、配置管理 1. 添加配置 (1). 在 nacos 界面中添加配置文件: 配置列表: 配置详情:…

【C++学习】C++11新特性(第二节)—— 右值引用与移动语义超详解

文章目录 文章简介二.右值引用1.什么是左值,什么是右值?什么是左值引用,什么是右值引用?2.左值引用与右值引用比较 三.右值引用使用场景和意义1.左值引用的使用场景:2.左值引用的短板:3.右值引用与移动构造…

Unity之Unity面试题(五)

内容将会持续更新,有错误的地方欢迎指正,谢谢! Unity之Unity面试题(五) TechX 坚持将创新的科技带给世界! 拥有更好的学习体验 —— 不断努力,不断进步,不断探索 TechX —— 心探索、心进取…

LVM逻辑卷

LVM逻辑卷 一.逻辑卷简介 LVM 是 Logical Volume Manager 的简称,译为中文就是逻辑卷管理。它是 Linux 下对硬盘分区的一种管理机制。LVM 适合于管理大存储设备,并允许用户动态调整文件系统的大小。此外,LVM 的快照功能可以帮助我们快速备份…

【AI大模型应用开发】【LangChain系列】 LangChain框架介绍,实现LangChain的Hello World

AI时代,相信大家或多或少都听过 LangChain 的大名。通俗的说,LangChain是一个面向大模型的开发框架(SDK)。 目前 LangChain 仍在快速迭代中,所以在使用中要时刻关注你所使用的版本和接口变更。 0. 认识LangChain框架 从…

Kafka 消费端消费重试和死信队列

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 Spring-Kafka 提供消费重试的机制。当消息消费失败的时候,Spring-Kafka …

【QT】pro文件里添加又删除LIBS不影响运行的原因

我发现个问题啊,如果运行项目,发现报错,缺少某dll,接着你在pro文件里加上win32:LIBS -lOpengl32(举个例子),接着可以运行了,接着把这行删掉,再运行,仍然可以…

中介者模式【行为模式C++】

1.简介 中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。 亦称: 调解人、控制器、Intermediary、Controller、Mediator 2.示例 中介者模式在…

计算机组成原理(IO,输入输出)

1、“821.2016T1(1)”,表示821真题,2016年的题,T1是 选择题/填空题/大题 的第一题,其他类似标记也是相通 2、个人小白总结自用,不一定适用于其他人,请自行甄别 3、有任何疑问,欢迎私信探讨&…

uniapp开发h5端使用video播放mp4格式视频黑屏,但有音频播放解决方案

mp4格式视频有一些谷歌播放视频黑屏,搜狗浏览器可以正常播放 可能和视频的编码格式有关,谷歌只支持h.264编码格式的视频播放 将mp4编码格式修改为h.264即可 转换方法: 如果是自己手动上传文件可以手动转换 如果是后端接口调取的地址就需…

【leetcode面试经典150题】36. 旋转图像(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主,题解使用C语言。(若有使用其他语言的同学也可了解题解思路,本质上语法内容一致&…

【Ansible自动化运维】Ansible入门基础信息【安装配置、常用命令与模块】

介绍安装配置注意事项yum安装验证安装配置host配置主机清单配置主控端被控端 常用模块命令组成command模块shell模块copy模块script模块 日志信息最后 介绍 Ansible 是一个开源 IT 自动化引擎,可自动执行供应、配置管理、应用程序部署、编排和许多其他 IT 流程。它可…

MySQL分库分表的方式有哪些

目录 一、为什么要分库分表 二、什么是分库分表 三、分库分表的几种方式 1.垂直拆分 2. 水平拆分 四、分库分表带来的问题 五、分库分表技术如何选型 一、为什么要分库分表 如果一个网站业务快速发展,那这个网站流量也会增加,数据的压力也会随之而…

护眼台灯哪个牌子好?护眼台灯十大排名,看看业内人怎么选!

眼镜已成为许多人日常生活中不可或缺的一部分,但戴眼镜并不总是方便的。现在许多家长也越来越关注孩子的视力问题,有些学生的书桌上已经放置上了护眼台灯。这种台灯提供柔和的光线,有助于改善照明环境,保护眼睛健康。然而&#xf…

使用jQuery实现购物界面的动态效果

实现功能:(购物车以表格的格式展示) 1 全选框和复选框之间的联动关系: 点击全选,所有复选框checked状态为true 点击复选框,全选框状态实时更新 2 点击删除按钮,删除对应的行 3 点击删除所选…

VUE3的有关知识

学习vue3的原因 在vue2当中的组件的实例,都是data一块,computed一块,当我们去找某一变量相关的则十分麻烦,vue3是组合式API,vue2是选项式, vue3的优点: 1)组合式更易维护 2)更快的速度 3)更小的体积 4)更好的响应式proxy 使用vue3相关脚手架创建项目 步骤: 1)node -v node版…

【CVE-2010-2883】进行钓鱼攻击的研究

最近作业中研究APT攻击,了解到2011年前后披露的LURID-APT,其中敌手利用了各种版本的文件查看器的漏洞实现攻击。CVE-2010-2883就是其中被利用的一个adobe reader的漏洞。特此复现,更好的研究和防范APT攻击。 本文仅仅是对相关漏洞利用的学习…

PyCharm Pro 2024:卓越的Python编辑开发工具,适用于Mac与Windows平台

PyCharm Pro 2024是一款专为Python开发者设计的强大编辑开发工具,无论是Mac还是Windows用户,都能从中受益良多。该软件凭借其出色的性能、丰富的功能和卓越的用户体验,成为Python编程界的翘楚。 作为一款高效的Python编辑器,PyCh…

02-结构化程式与自定义函数

视频教程:b站视频【MATLAB教程_台大郭彦甫(14课)原视频补档】https://www.bilibili.com/video/BV1GJ41137UH/?share_sourcecopy_web&vd_sourc*ed6b9f96888e9c85118cb40c164875dfc 官网教程: MATLAB 快速入门 - MathWorks 中…