24.8.3学习笔记

梯度指示的方向是各点处的函数值减小最多的方向

构建一个二层神经网络

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 将父目录添加到系统路径,以便导入父目录中的文件

from common.functions import *
from common.gradient import numerical_gradient

# 定义一个名为 TwoLayerNet 的类,用于构建两层神经网络
class TwoLayerNet:

    # 初始化方法,在创建对象时自动调用
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        # 初始化神经网络的参数
        self.params = {}  #创建一个空字典
        # 随机初始化第一层的权重矩阵,使用正态分布,标准差为 weight_init_std
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        # 初始化第一层的偏置向量为全零
        self.params['b1'] = np.zeros(hidden_size)
        # 随机初始化第二层的权重矩阵,使用正态分布,标准差为 weight_init_std
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        # 初始化第二层的偏置向量为全零
        self.params['b2'] = np.zeros(output_size)

    # 预测方法,输入数据 x 进行前向传播得到预测结果
    def predict(self, x):
        W1, W2 = self.params['W1'], self.params['W2']  # 获取第一层和第二层的权重矩阵
        b1, b2 = self.params['b1'], self.params['b2']  # 获取第一层和第二层的偏置向量

        # 第一层的线性计算和激活函数
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)

        # 第二层的线性计算和 softmax 函数得到最终预测结果
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)

        return y

    # 计算损失的方法,输入数据 x 和监督数据 t
    def loss(self, x, t):
        y = self.predict(x)  # 进行预测得到预测结果

        # 计算交叉熵损失
        return cross_entropy_error(y, t)

    # 计算准确率的方法,输入数据 x 和监督数据 t
    def accuracy(self, x, t):
        y = self.predict(x)  # 进行预测得到预测结果
        y = np.argmax(y, axis=1)  # 找出预测结果中每个样本的最大概率类别
        t = np.argmax(t, axis=1)  # 找出监督数据中每个样本的真实类别
        #预测类别: [1 0 2 0 1]
        #真实类别: [2 0 1 0 1]
        #共五个列表,预测类别意思是:预测分别第1,0,2,0,1个是正确的
        #用这个和真实类别去做比较,即可得出准确率
        # 计算准确率
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy

    # 数值微分求梯度(out)
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)  # 定义一个以权重 W 为参数的损失函数,调用了上面的方法

        grads = {}  # 用于存储梯度的字典,权重和偏置是键,梯度是其对应的值
        # 使用数值梯度方法计算第一层权重的梯度
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])#将损失函数和初始权重值(偏置值)传入
        # 可以得到针对这个损失函数以及特定的参数(权重偏执)所得到的梯度
        # 使用数值梯度方法计算第一层偏置的梯度
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        # 使用数值梯度方法计算第二层权重的梯度
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        # 使用数值梯度方法计算第二层偏置的梯度
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

        return grads

    # 计算梯度的方法(误差反向传播法),输入数据 x 和监督数据 t
    def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']  # 获取第一层和第二层的权重矩阵
        b1, b2 = self.params['b1'], self.params['b2']  # 获取第一层和第二层的偏置向量
        grads = {}  # 用于存储梯度的字典

        batch_num = x.shape[0]  # 获取批量数据的数量

        # 前向传播
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)

        # 反向传播
        dy = (y - t) / batch_num  # 计算输出层的误差
        grads['W2'] = np.dot(z1.T, dy)  # 计算第二层权重的梯度
        grads['b2'] = np.sum(dy, axis=0)  # 计算第二层偏置的梯度

        da1 = np.dot(dy, W2.T)  # 计算第一层的误差
        dz1 = sigmoid_grad(a1) * da1  # 计算第一层激活函数的导数乘误差
        grads['W1'] = np.dot(x.T, dz1)  # 计算第一层权重的梯度
        grads['b1'] = np.sum(dz1, axis=0)  # 计算第一层偏置的梯度

        return grads

下图是整个学习过程 (调用了上图中的类里的方法,创建了实例)

# coding: utf-8
import sys, os
# 导入系统和操作系统相关的模块
sys.path.append(os.pardir)  
# 将父目录添加到系统路径,以便能够导入父目录中的模块或文件

import numpy as np
# 导入 NumPy 库,用于数值计算和数组操作
import matplotlib.pyplot as plt
# 导入 Matplotlib 的 pyplot 模块,用于绘图

from dataset.mnist import load_mnist
# 从 'dataset.mnist' 模块中导入 'load_mnist' 函数,用于加载 MNIST 数据集

from two_layer_net import TwoLayerNet
# 从 'two_layer_net' 模块中导入 'TwoLayerNet' 类,用于构建两层神经网络

# 读入 MNIST 数据集,并进行数据标准化和独热编码标签处理
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)  

# 创建一个 TwoLayerNet 类的实例,设置输入层大小、隐藏层大小和输出层大小
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10) # 调用了二层神经网络

# 设定训练的迭代次数
iters_num = 10000  

# 获取训练集数据的数量
train_size = x_train.shape[0]  

# 设定每次训练使用的小批量数据的大小
batch_size = 100  # 迭代10000次,一批为100.

# 设定学习率
learning_rate = 0.1  

# 初始化用于存储训练损失值的列表
train_loss_list = []  

# 初始化用于存储训练准确率的列表
train_acc_list = []  

# 初始化用于存储测试准确率的列表
test_acc_list = []  

# 计算每个 epoch 包含的迭代次数,每轮epoch都要遍历整个数据集的数据,故总量÷一批
# 也就计算出每轮epoch要迭代训练多少次
iter_per_epoch = max(train_size / batch_size, 1)  

# 开始训练的循环
for i in range(iters_num):
    # 从训练集数据数量中随机选择 batch_size 个索引,也就是100个
    batch_mask = np.random.choice(train_size, batch_size)  
    # 根据随机选择的索引获取小批量的训练数据
    x_batch = x_train[batch_mask]  
    # 根据随机选择的索引获取小批量的训练标签
    t_batch = t_train[batch_mask]  

    # 可以计算出关于这一小批数据的梯度
    # grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)  
    # 通过循环更新神经网络的参数
    for key in ('W1', 'b1', 'W2', 'b2'):
        # 从当前参数的值中减去学习率乘以对应梯度的值,以实现对参数的调整,从而使神经网络在训练过程中逐渐优化性能。
        #  若梯度值为正,也就是说损失函数曲线向上的,递增的,故应该减少当前参数,使得损失函数的值变小(逆向)
         network.params[key] -= learning_rate * grad[key]
    # 计算当前小批量数据的损失值,由于network是哪个二层神经元的实例,故可以直接调用那个类里的方法
    loss = network.loss(x_batch, t_batch) # 计算交叉熵损失
    # 将损失值添加到列表中
    train_loss_list.append(loss)  

    # 如果当前迭代次数是每个 epoch 迭代次数的整数倍,说明该换轮了
    if i % iter_per_epoch == 0:
        # 计算整个训练集的准确率,非batch
        train_acc = network.accuracy(x_train, t_train)  
        # 计算测试集的准确率
        test_acc = network.accuracy(x_test, t_test)  
        # 将训练集准确率添加到列表
        train_acc_list.append(train_acc)  
        # 将测试集准确率添加到列表
        test_acc_list.append(test_acc)  
        # 打印训练集和测试集的准确率
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))  

# 定义绘图的标记
markers = {'train': 'o', 'test': 's'}  
# 创建表示 epochs 的 x 轴数据
x = np.arange(len(train_acc_list))  
# 绘制训练集准确率曲线,并添加标签
plt.plot(x, train_acc_list, label='train acc')  
# 绘制测试集准确率曲线,虚线,并添加标签
plt.plot(x, test_acc_list, label='test acc', linestyle='--')  
# 设置 x 轴标签
plt.xlabel("epochs")  
# 设置 y 轴标签
plt.ylabel("accuracy")  
# 设置 y 轴范围
plt.ylim(0, 1.0)  
# 显示图例,位置在右下角
plt.legend(loc='lower right')  
# 显示图形
plt.show()  

数据准备

  • 使用load_mnist函数加载MNIST数据集,并对数据进行预处理,包括归一化输入数据(使每个像素值处于0到1之间)和将标签转换为one-hot编码形式。
  • 加载的数据被分为训练集和测试集,训练集包含60,000张图像,测试集包含10,000张图像。

模型定义

  • 实例化TwoLayerNet类(代码图1),创建一个两层神经网络模型。
  • 输入层大小为784(对应28x28像素的图像),隐藏层大小为50,输出层大小为10(对应10个数字类别)。

训练设置

  • 设定训练的总迭代次数为10,000次。
  • 每次迭代使用的小批量数据大小(batch size)为100。
  • 学习率为0.1。
  • 初始化列表来记录训练过程中的损失值和准确率。(方便查看,绘图用)

训练过程

  • 在给定的迭代次数内,每次迭代执行以下步骤:
    • 随机抽取一个小批量的训练数据及其对应的标签。
    • 计算该小批量数据的梯度。
    • 利用这些梯度来更新网络的参数(权重和偏置),这里使用的是随机梯度下降(SGD)算法。
    • 计算该小批量数据的损失值,并将其添加到损失值列表中。
    • 每完成一个epoch(即遍历完整个训练集),计算并记录训练集和测试集上的准确率,并打印这些准确率。

可视化结果

  • 使用Matplotlib绘制训练集和测试集准确率随训练周期变化的趋势图。

具体细节

  1. 数据加载与预处理

    • load_mnist(normalize=True, one_hot_label=True):加载MNIST数据集,对数据进行归一化处理,并将标签转换为one-hot编码形式。
  2. 网络初始化

    • TwoLayerNet(input_size=784, hidden_size=50, output_size=10):创建一个两层神经网络,输入层接收784维的向量(28x28像素的图像展平后的向量),隐藏层有50个神经元,输出层有10个神经元(分别对应0-9这10个数字)。
  3. 训练过程

    • 小批量随机梯度下降:每次迭代都从训练集中随机抽取100个样本组成一个小批量。
    • 梯度计算:使用network.gradient(x_batch, t_batch)计算梯度。这个方法可能使用了反向传播算法。
    • 参数更新:使用SGD算法更新网络参数。对于每一个权重矩阵和偏置向量,通过减去学习率乘以相应的梯度来更新其值。
    • 损失计算:在每次迭代中,计算当前小批量数据的损失值,并将其添加到train_loss_list列表中。
    • 准确率计算:每完成一个epoch,计算训练集和测试集的准确率,并将它们分别添加到train_acc_listtest_acc_list列表中。
  4. 结果可视化

    • 绘制训练集和测试集准确率随训练周期变化的趋势图。通过这种方式,可以直观地看到模型在训练过程中的表现以及是否出现过拟合现象。

文件的读取和写入(比较简单):

def read_file_example(filename):
    # 使用 'with' 语句自动管理文件的打开和关闭
    with open(filename, 'r') as file:
        # 一次性读取全部内容
        all_content = file.read()  # 读取文件的所有内容
        print("All Content:")  # 输出提示信息
        print(all_content)  # 打印文件内容

        # 回到文件开始位置,以便可以再次读取
        file.seek(0)  # 将文件指针移到文件的开头

        # 逐行读取
        print("\nReading Line by Line:")  # 输出提示信息
        for line in file:  # 遍历文件中的每一行
            print(line.strip())  # 打印去除行尾换行符的每一行

        # 回到文件开始位置,以便可以再次读取
        file.seek(0)  # 将文件指针移到文件的开头

        # 读取特定数量的字符
        print("\nFirst 10 Characters:")  # 输出提示信息
        print(file.read(10))  # 读取文件的前10个字符并打印

        # 回到文件开始位置,以便可以再次读取
        file.seek(0)  # 将文件指针移到文件的开头

        # 读取所有行到列表
        print("\nLines as List:")  # 输出提示信息
        lines = file.readlines()  # 读取文件中的所有行,并将每一行作为一个字符串元素存入列表
        for line in lines:  # 遍历列表中的每一行
            print(line.strip())  # 打印去除行尾换行符的每一行

# 调用函数,传入文件名
filename = 'testpy.txt'
read_file_example(filename)

import os


def write_to_file(filename, data):
    """
    写入文件的函数。

    # :param filename: 文件名
    # :param data: 要写入的数据
    """
    # 使用 'with' 语句自动管理文件的打开和关闭
    with open(filename, 'w', encoding='utf-8') as file:
        # 写入数据
        file.write(data)
        print(f"Data written to file: {filename}")


def append_to_file(filename, data):
    """
    向文件追加数据的函数。

    :param filename: 文件名
    :param data: 要追加的数据
    """
    # 使用 'with' 语句自动管理文件的打开和关闭
    with open(filename, 'a', encoding='utf-8') as file:
        # 追加数据
        file.write(data)
        print(f"Data appended to file: {filename}")


def write_binary_data(filename, data):
    """
    写入二进制数据的函数。

    :param filename: 文件名
    :param data: 要写入的二进制数据
    """
    # 使用 'with' 语句自动管理文件的打开和关闭
    with open(filename, 'wb') as file:
        # 写入二进制数据
        file.write(data)
        print(f"Binary data written to file: {filename}")


# 调用函数
if __name__ == '__main__':
    # 文本数据写入
    text_filename = 'testpy.txt'
    text_data = "Hello, this is an example of writing text to a file.\n"
    write_to_file(text_filename, text_data)

    # 追加文本数据
    append_text_data = "\nThis text was appended to the file."
    append_to_file(text_filename, append_text_data)

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值