当前位置: 首页 > news >正文

做零售出口的网站网站的后缀名

做零售出口的网站,网站的后缀名,哪里的网站建设,做微网站价格手写数字识别#xff08;神经网络入门#xff09; 文章目录 手写数字识别#xff08;神经网络入门#xff09;实验概述实验过程数据准备模型实现线性变换层前向传播反向传播更新参数整体实现 激活函数层#xff08;ReLU#xff09;前向传播反向传播整体实现 Softmax层神经网络入门 文章目录 手写数字识别神经网络入门实验概述实验过程数据准备模型实现线性变换层前向传播反向传播更新参数整体实现 激活函数层ReLU前向传播反向传播整体实现 Softmax层含交叉熵损失函数前向传播交叉熵损失反向传播整体实现 含单隐藏层的前馈神经网络MLP 训练评估 实验概述 仅使用numpy和pytorch中的tensor库以及torch中相关基本计算方法手动实现含单隐藏层的前馈神经网络模型以及反向梯度传播的学习过程来进行MNIST数据集上的手写数字识别。 实验过程 数据准备 首先在 MNIST官网 下载手写数字数据集保存至本地 data_folder 并解压 import gzip import shutil import osdata_folder ../datafor file_name in os.listdir(data_folder):if file_name.endswith(.gz):file_path os.path.join(data_folder, file_name)out_path file_path.replace(.gz, ) # 解压后的文件路径# 解压文件with gzip.open(file_path, rb) as in_file:with open(out_path, wb) as out_file:shutil.copyfileobj(in_file, out_file)根据官网提供的数据格式说明载入图片和标签数据 import numpy as npdef load_images(file_path): 载入图片数据 with open(file_path, rb) as f:f.read(16) # 根据MNIST数据集格式跳过元数据data np.frombuffer(f.read(), dtypenp.uint8)data data.reshape(-1, 28, 28) # 每张图片为 28x28 的灰度值图片return datadef load_labels(file_path): 载入标签数据 with open(file_path, rb) as f:f.read(8) # 根据MNIST数据集格式跳过元数据labels np.frombuffer(f.read(), dtypenp.uint8)return labelstrain_images load_images(data_folder /train-images-idx3-ubyte) train_labels load_labels(data_folder /train-labels-idx1-ubyte) test_images load_images(data_folder /t10k-images-idx3-ubyte) test_labels load_labels(data_folder /t10k-labels-idx1-ubyte)可视化一部分数据作检查 import matplotlib.pyplot as pltdef visualize(images, labels, num_samples10):plt.figure(figsize(10, 2))for i in range(num_samples):plt.subplot(1, num_samples, i 1)plt.imshow(images[i], cmapgray)plt.title(fLabel: {labels[i]})plt.axis(off)plt.show()visualize(train_images, train_labels) # 可视化训练集的前10张图像 visualize(test_images, test_labels) # 可视化测试集的前10张图像可以看到数据已经正确载入 下面进行一些数据预处理。 为了加速后续计算将数据放到tensor中 import torchmy_device torch.device(cuda if torch.cuda.is_available() else cpu)train_images_tensor torch.tensor(train_images, dtypetorch.float32, devicemy_device) train_labels_tensor torch.tensor(train_labels, dtypetorch.long, devicemy_device) test_images_tensor torch.tensor(test_images, dtypetorch.float32, devicemy_device) test_labels_tensor torch.tensor(test_labels, dtypetorch.long, devicemy_device)将数据展平并归一化便于后续神经网络的数据输入 train_images_tensor train_images_tensor.flatten(start_dim1) / 255.0 test_images_tensor test_images_tensor.flatten(start_dim1) / 255.0手动实现Dataset和DataLoader类便于后续训练时按批次加载数据 class MyDataset:def __init__(self, images, labels):self.images imagesself.labels labelsdef __len__(self):return len(self.images)def __getitem__(self, id):return self.images[id], self.labels[id]class MyDataLoader:def __init__(self, dataset, batch_size1, shuffleFalse, devicecpu):self.dataset datasetself.batch_size batch_sizeself.shuffle shuffleself.ids np.arange(len(dataset)) # 索引列表self.cur_id 0self.device devicedef __iter__(self):self.cur_id 0 # 重置索引if self.shuffle:np.random.shuffle(self.ids) # 打乱索引return selfdef __next__(self):if self.cur_id len(self.dataset):raise StopIteration# 获取一个batch的索引batch_ids self.ids[self.cur_id : self.cur_id self.batch_size]# 获取一个batch的对应数据batch_data [self.dataset[i] for i in batch_ids]# 返回一个batch的图片和标签并改为tensor类型images, labels zip(*batch_data)images torch.stack(images).to(self.device)labels torch.tensor(labels).to(self.device)self.cur_id self.batch_sizereturn images, labelsdef __len__(self): 数据集的大小即总批次数量 return int(np.ceil(len(self.dataset) / self.batch_size)) 用上述DataLoader包装数据 train_set MyDataset(train_images_tensor, train_labels_tensor) test_set MyDataset(test_images_tensor, test_labels_tensor)train_loader MyDataLoader(train_set, batch_size64, shuffleTrue, devicemy_device) test_loader MyDataLoader(test_set, batch_size64, shuffleFalse, devicemy_device)模型实现 首先需要实现线性层、激活函数ReLU、Softmax函数、损失函数交叉熵损失等组件。 同时由于后续神经网络中的前向反向传播也要手动实现故上述组件的 forward 和 backward 方法也需要先定义好。 线性变换层 前向传播 线性变换的数学定义为 y x W T b y xW^T b yxWTb 其中 x x x 是大小为 i n p u t _ s i z e input\_size input_size 输入向量 W W W 是形状为 ( o u t p u t _ s i z e , i n p u t _ s i z e ) (output\_size, input\_size) (output_size,input_size) 的权重矩阵 b b b 是大小为 o u t p u t _ s i z e output\_size output_size 的偏置项 y y y 是大小为 o u t p u t _ s i z e output\_size output_size 的输出向量 据此可写出线性层的前向传播代码 def forward(self, x): 前向传播 self.x x.to(self.device)y torch.matmul(x, self.weights.T) self.biasreturn y反向传播 在反向传播过程中需要计算各参数即 W W W 和 b b b 的梯度以更新参数并计算本层输入的梯度传回上一层。 首先损失 L L L 对线性层输出 y y y 的梯度为 ∂ L ∂ y \frac{\partial L}{\partial y} ∂y∂L​ 该梯度会由下一层反向传播回来。 后续代码中下一层传回本层的梯度均记作 grad_out out表示是对本层输出的梯度 计算 L L L 对 W W W 的梯度 ∂ L ∂ W \frac{\partial L}{\partial W} ∂W∂L​ 根据链式法则有 ∂ L ∂ W ∂ L ∂ y ⋅ ∂ y ∂ W \frac{\partial L}{\partial W} \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial W} ∂W∂L​∂y∂L​⋅∂W∂y​ 其中 ∂ y ∂ W \frac{\partial y}{\partial W} ∂W∂y​ 即在线性变换 y x W T b y xW^T b yxWTb 中对 W W W 求偏导易得结果为 x x x 所以 W W W 的反向传播公式为 ∂ L ∂ W ∂ L ∂ y ⋅ x \frac{\partial L}{\partial W} \frac{\partial L}{\partial y} \cdot x ∂W∂L​∂y∂L​⋅x 代码层面考虑按批次训练则 ∂ L ∂ y \frac{\partial L}{\partial y} ∂y∂L​ 是形状为 ( b a t c h _ s i z e , o u t p u t _ s i z e ) (batch\_size, output\_size) (batch_size,output_size) 的梯度矩阵 x x x 是形状为 ( b a t c h _ s i z e , i n p u t _ s i z e ) (batch\_size, input\_size) (batch_size,input_size) 的输入矩阵故它们相乘时前者需要转置 self.grad_weights torch.matmul(grad_out.T, self.x)计算 L L L 对 b b b 的梯度 ∂ L ∂ b \frac{\partial L}{\partial b} ∂b∂L​ 根据链式法则有 ∂ L ∂ b ∂ L ∂ y ⋅ ∂ y ∂ b \frac{\partial L}{\partial b} \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial b} ∂b∂L​∂y∂L​⋅∂b∂y​ 其中 ∂ y ∂ b \frac{\partial y}{\partial b} ∂b∂y​ 即在线性变换 y x W T b y xW^T b yxWTb 中对 b b b 求偏导易得结果为 1 1 1 所以 b b b 的反向传播公式为 ∂ L ∂ b ∂ L ∂ y \frac{\partial L}{\partial b} \frac{\partial L}{\partial y} ∂b∂L​∂y∂L​ 代码层面考虑按批次训练即 ∂ L ∂ b ∑ i 1 b a t c h _ s i z e ∂ L ∂ y i \frac{\partial L}{\partial b} \sum^{batch\_size}_{i 1}\frac{\partial L}{\partial y_i} ∂b∂L​∑i1batch_size​∂yi​∂L​ 有 self.grad_bias torch.sum(grad_out, dim0)计算 L L L 对线性层输入 x x x 的梯度 ∂ L ∂ x \frac{\partial L}{\partial x} ∂x∂L​ 根据链式法则有: ∂ L ∂ x ∂ L ∂ y ⋅ ∂ y ∂ x \frac{\partial L}{\partial x} \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial x} ∂x∂L​∂y∂L​⋅∂x∂y​ 其中 ∂ y ∂ x \frac{\partial y}{\partial x} ∂x∂y​ 即在线性变换 y x W T b y xW^T b yxWTb 中对 x x x 求偏导易得结果为 W W W 所以 x x x 的反向传播公式为 ∂ L ∂ x ∂ L ∂ y ⋅ W \frac{\partial L}{\partial x} \frac{\partial L}{\partial y} \cdot W ∂x∂L​∂y∂L​⋅W 相应代码为 grad_in torch.matmul(grad_out, self.weights)后续代码中本层传给上一层的梯度均记作 grad_in in表示是对本层输入的梯度 更新参数 根据上面计算出的参数梯度按照梯度下降法更新参数 W ← W − η ⋅ ∂ L ∂ W b ← b − η ⋅ ∂ L ∂ b W \leftarrow W - \eta \cdot \frac{\partial L}{\partial W} \\ b \leftarrow b - \eta \cdot \frac{\partial L}{\partial b} W←W−η⋅∂W∂L​b←b−η⋅∂b∂L​ 其中 η \eta η 为学习率。 据此写出相应代码为 def update(self, lr): 更新参数 self.weights self.weights - lr * self.grad_weightsself.bias self.bias - lr * self.grad_bias其中 lr 为学习率。 整体实现 综上线性层组件的整体代码实现为 class MyLinear(): 线性变换层Args:input_size (int): 输入特征的数量output_size (int): 输出特征的数量Examples: input torch.randn(128, 20) m MyLinear(20, 30) output m.forward(input) print(output.size())torch.Size([128, 30])def __init__(self, input_size, output_size, devicecpu):# 正态随机初始化权重和偏置self.device deviceself.weights torch.randn(output_size, input_size, deviceself.device)self.bias torch.randn(output_size, deviceself.device)def forward(self, x): 前向传播 self.x x.to(self.device)y torch.matmul(x, self.weights.T) self.biasreturn ydef backward(self, grad_out): 反向传播 self.grad_weights torch.matmul(grad_out.T, self.x)self.grad_bias torch.sum(grad_out, dim0)grad_in torch.matmul(grad_out, self.weights)return grad_indef update(self, lr): 更新参数 self.weights self.weights - lr * self.grad_weightsself.bias self.bias - lr * self.grad_bias激活函数层ReLU 前向传播 ReLU函数的数学定义为 y R e L U ( x ) { x , x 0 0 , x ≤ 0 y ReLU(x) \begin{cases} x, \ x 0 \\ 0, \ x \le 0 \end{cases} yReLU(x){x, x00, x≤0​ 据此可写出ReLU层的前向传播代码 def forward(self, x):self.x xy torch.maximum(x, torch.tensor(0.0))return y反向传播 计算ReLU层的梯度即损失 L L L 对输入 x x x 的偏导数 ∂ L ∂ x \frac{\partial L}{\partial x} ∂x∂L​ 。由于反向传播过程中下一层会传回 L L L 对ReLU输出 y y y 的梯度 ∂ L ∂ y \frac{\partial L}{\partial y} ∂y∂L​ 故根据链式法则ReLU的梯度可以写成 ∂ L ∂ x ∂ L ∂ y ⋅ ∂ y ∂ x \frac{\partial L}{\partial x} \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial x} ∂x∂L​∂y∂L​⋅∂x∂y​ 其中 ∂ y ∂ x \frac{\partial y}{\partial x} ∂x∂y​ 即对 R e L U ( x ) ReLU(x) ReLU(x) 求导易得 ∂ y ∂ x { 1 , x 0 0 , x ≤ 0 \frac{\partial y}{\partial x} \begin{cases} 1, \ x 0 \\ 0, \ x \le 0 \end{cases} ∂x∂y​{1, x00, x≤0​ 所以ReLU的反向传播公式为 ∂ L ∂ x ∂ L ∂ y ⋅ { 1 , x 0 0 , x ≤ 0 { ∂ L ∂ y , x 0 0 , x ≤ 0 \frac{\partial L}{\partial x} \frac{\partial L}{\partial y} \cdot \begin{cases} 1, \ x 0 \\ 0, \ x \le 0 \end{cases} \begin{cases} \frac{\partial L}{\partial y}, \ x 0 \\ 0, \quad x \le 0 \\ \end{cases} ∂x∂L​∂y∂L​⋅{1, x00, x≤0​{∂y∂L​, x00,x≤0​ 根据上面的公式可以写出反向传播代码 def backward(self, grad_output):grad_input grad_output.clone()grad_input[self.x 0] 0 # 对于非正的输入梯度为零return grad_input整体实现 综上ReLU组件的整体代码实现为 class ReLU:def forward(self, x):self.x x # 保存输入以便后向传播使用return torch.maximum(x, torch.tensor(0.0))def backward(self, grad_output):grad_input grad_output.clone()grad_input[self.x 0] 0 # 对于小于零的输入梯度为零return grad_inputSoftmax层含交叉熵损失函数 前向传播 Softmax函数的数学定义为 对于输入向量 x [ x 1 , x 2 , … , x n ] \ x [x_1, \ x_2, \dots, \ x_n]  x[x1​, x2​,…, xn​] Softmax的输出向量 y ^ [ y ^ 1 , y ^ 2 , … , y ^ n ] \hat{y} [\hat{y}_1, \ \hat{y}_2, \dots, \ \hat{y}_n] y^​[y^​1​, y^​2​,…, y^​n​] 中的每个元素 y ^ i \hat{y}_i y^​i​ 有 y ^ i S o f t m a x ( x i ) e x i ∑ j 1 n e x j , j 1 , 2 , … , n \hat{y}_i Softmax(x_i) \frac{e^{x_i}}{\sum^{n}_{j1} e^{x_j}}, \ \ j 1,2,\dots,n y^​i​Softmax(xi​)∑j1n​exj​exi​​,  j1,2,…,n 故Softmax的输出 y ^ \hat{y} y^​ 是一个概率分布代码中将其记作 probs 据此可写出其前向传播代码 def forward(self, x):x_max torch.max(x, dim1, keepdimTrue) # 防止溢出减去最大值来增加数值稳定性x_exp torch.exp(x - x_max.values)self.probs x_exp / torch.sum(x_exp, dim1, keepdimTrue)return self.probs交叉熵损失 交叉熵cross entropy的数学定义为 H ( p , q ) − ∑ x p ( x ) log ⁡ q ( x ) H(p, q) - \sum_{x}p(x) \log{q(x)} H(p,q)−x∑​p(x)logq(x) 其中 p , q p, q p,q 是两个概率分布。因此本实验中可以先将实际标签进行one-hot编码则对于每个样本其交叉熵损失函数为 L ( y , y ^ ) − ∑ j 1 C y j log ⁡ y j ^ L(y, \ \hat{y}) -\sum^{C}_{j1} y_j \log{\hat{y_j}} L(y, y^​)−j1∑C​yj​logyj​^​ 其中 C C C 是类别数本实验中为10 y y y 是该样本的真实标签one-hot分布 y j y_j yj​ 表示其在标签 j j j 上的one-hot值0或1 y ^ \hat{y} y^​ 是该样本的预测标签概率分布 y j ^ \hat{y_j} yj​^​ 表示预测的该样本属于标签 j j j 的概率由softmax得出 因此对于每一批次的 N N N 个样本可得其mini-batch损失为 L − 1 N ∑ i 1 N ∑ j 1 C y j log ⁡ y j ^ L - \frac{1}{N} \sum^{N}_{i1} \sum^{C}_{j1}{y_j \log{\hat{y_j}}} L−N1​i1∑N​j1∑C​yj​logyj​^​ 据此可写出交叉熵损失函数代码 def cross_entropy(self, predictions, truths):probs self.probs # Softmax得到的预测标签的概率分布# 设定一个最小值min_val防止log(0)min_val 1e-6probs torch.where(probs min_val, torch.full_like(probs, min_val), probs)# 计算交叉熵损失batch_size predictions.shape[0]truth_onehot torch.zeros(batch_size, probs.shape[1], devicemy_device)truth_onehot[torch.arange(batch_size), truths] 1loss -torch.mean(torch.sum(truth_onehot * torch.log(probs), dim1))self.batch_size batch_size # 批次大小self.truth_onehot truth_onehot # 真实标签的one-hot分布return loss反向传播 计算Softmax层的梯度即 L L L 对输入 x x x 的偏导数 ∂ L ∂ x \frac{\partial{L}}{\partial{x}} ∂x∂L​ 。根据链式法则可将其写成 ∂ L ∂ x ∂ L ∂ y ^ ⋅ ∂ y ^ ∂ x \frac{\partial L}{\partial x} \frac{\partial L}{\partial{\hat{y}}} \cdot \frac{\partial{\hat{y}}}{\partial x} ∂x∂L​∂y^​∂L​⋅∂x∂y^​​ 故对于 x x x 中的某个样本 x i x_i xi​ 有 ∂ L ∂ x i ∂ L ∂ y ^ ⋅ ∂ y ^ ∂ x i ( 1 ) \frac{\partial L}{\partial x_i} \frac{\partial L}{\partial{\hat{y}}} \cdot \frac{\partial{\hat{y}}}{\partial x_i} \quad (1) ∂xi​∂L​∂y^​∂L​⋅∂xi​∂y^​​(1) 其中 y ^ \hat{y} y^​ 是Softmax层的输出。根据上面的Softmax公式对于某个样本有 ∂ L ∂ y ^ j ∂ ( − ∑ j 1 C y j log ⁡ y ^ j ) ∂ y ^ j − y j y ^ j ( 2 ) \frac{\partial L}{\partial{\hat{y}_j}} \frac{\partial{(-\sum^{C}_{j1} y_j \log{\hat{y}_j}})}{\partial{\hat{y}_j}} - \frac{y_j}{\hat{y}_j} \quad (2) ∂y^​j​∂L​∂y^​j​∂(−∑j1C​yj​logy^​j​)​−y^​j​yj​​(2) 将 ( 1 ) (1) (1) 展开后可带入 ( 2 ) (2) (2) ∂ L ∂ x i ∂ L ∂ y ^ ⋅ ∂ y ^ ∂ x i ∑ j 1 n ∂ L ∂ y ^ j ⋅ ∂ y ^ j ∂ x i − ∑ j 1 n y j y ^ j ⋅ ∂ y ^ j ∂ x i ( 3 ) \frac{\partial L}{\partial x_i} \frac{\partial L}{\partial{\hat{y}}} \cdot \frac{\partial{\hat{y}}}{\partial x_i} \sum^{n}_{j1} \frac{\partial L}{\partial{\hat{y}_j}} \cdot \frac{\partial{\hat{y}_j}}{\partial x_i} - \sum^{n}_{j1} \frac{y_j}{\hat{y}_j} \cdot \frac{\partial{\hat{y}_j}}{\partial x_i} \quad (3) ∂xi​∂L​∂y^​∂L​⋅∂xi​∂y^​​j1∑n​∂y^​j​∂L​⋅∂xi​∂y^​j​​−j1∑n​y^​j​yj​​⋅∂xi​∂y^​j​​(3) 不妨假设该样本的正确标签为 k k k 即one-hot编码后的 y [ y 1 , y 2 , … , y n ] y [y_1, \ y_2, \dots, \ y_n] y[y1​, y2​,…, yn​] 中只有 y k 1 y_k 1 yk​1 、其他 y j 0 y_j 0 yj​0 则 ( 3 ) (3) (3) 可以进一步化简为 ∂ L ∂ x i − y k y ^ k ⋅ ∂ y ^ k ∂ x i ( 4 ) \frac{\partial L}{\partial x_i} - \frac{y_k}{\hat{y}_k} \cdot \frac{\partial{\hat{y}_k}}{\partial x_i} \quad (4) ∂xi​∂L​−y^​k​yk​​⋅∂xi​∂y^​k​​(4) 显然接下来需要计算 ∂ y ^ k ∂ x k \frac{\partial{\hat{y}_k}}{\partial x_k} ∂xk​∂y^​k​​ 。需要分两种情况 当 i k i k ik ∂ y ^ k ∂ x i ∂ y ^ k ∂ x k ∂ ( e x k ∑ j 1 n e x j ) ∂ x k e x k ( ∑ j 1 n e x j ) − ( e x k ) 2 ( ∑ j 1 n e x j ) 2 e x k ∑ j 1 n e x j − ( e x k ∑ j 1 n e x j ) 2 y ^ k − y ^ k 2 y ^ k ( 1 − y ^ k ) ( 5 ) \begin{aligned} \frac{\partial{\hat{y}_k}}{\partial x_i} \frac{\partial{\hat{y}_k}}{\partial x_k} \\ \frac{\partial{(\frac{e^{x_k}}{\sum^{n}_{j1} e^{x_j}}})}{\partial{x_k}} \\ \frac{e^{x_k}(\sum^{n}_{j1}{e^{x_j}}) - (e^{x_k})^2}{(\sum^{n}_{j1}{e^{x_j}})^2} \\ \frac{e^{x_k}}{\sum^{n}_{j1} e^{x_j}} - (\frac{e^{x_k}}{\sum^{n}_{j1}{e^{x_j}}})^2 \\ \hat{y}_k - {\hat{y}_k}^2 \\ \hat{y}_k(1 - \hat{y}_k) \end{aligned} \quad (5) ∂xi​∂y^​k​​​∂xk​∂y^​k​​∂xk​∂(∑j1n​exj​exk​​)​(∑j1n​exj​)2exk​(∑j1n​exj​)−(exk​)2​∑j1n​exj​exk​​−(∑j1n​exj​exk​​)2y^​k​−y^​k​2y^​k​(1−y^​k​)​(5) 当 i ≠ k i \neq k ik ∂ y ^ k ∂ x i ∂ ( e x k ∑ j 1 n e x j ) ∂ x i − e x k ⋅ e x i ( ∑ j 1 n e x j ) 2 − e x k ∑ j 1 n e x j ⋅ e x i ∑ j 1 n e x j − y ^ k ⋅ y ^ i ( 6 ) \begin{aligned} \frac{\partial{\hat{y}_k}}{\partial x_i} \frac{\partial{(\frac{e^{x_k}}{\sum^{n}_{j1} e^{x_j}}})}{\partial{x_i}} \\ \frac{-e^{x_k} \cdot e^{x_i}}{(\sum^{n}_{j1}{e^{x_j}})^2} \\ - \frac{e^{x_k}}{\sum^{n}_{j1} e^{x_j}} \cdot \frac{e^{x_i}}{\sum^{n}_{j1}{e^{x_j}}} \\ - \hat{y}_k \cdot \hat{y}_i \end{aligned} \quad (6) ∂xi​∂y^​k​​​∂xi​∂(∑j1n​exj​exk​​)​(∑j1n​exj​)2−exk​⋅exi​​−∑j1n​exj​exk​​⋅∑j1n​exj​exi​​−y^​k​⋅y^​i​​(6) 将 ( 5 ) , ( 6 ) (5), (6) (5),(6) 带入 ( 4 ) (4) (4) 得 ∂ L ∂ x i { − y k y ^ k ⋅ y ^ k ( 1 − y ^ k ) − y k ( 1 − y ^ k ) y ^ k − 1 , i k − y k y ^ k ⋅ ( − y ^ k ⋅ y ^ i ) y k ⋅ y ^ i y ^ i , i ≠ k \frac{\partial L}{\partial x_i} \begin{cases} {-} \frac{y_k}{\hat{y}_k} \cdot \hat{y}_k(1 - \hat{y}_k) -y_k(1 - \hat{y}_k) \hat{y}_k - 1, \quad ik \\ {-} \frac{y_k}{\hat{y}_k} \cdot (-\hat{y}_k \cdot \hat{y}_i) y_k \cdot \hat{y}_i \hat{y}_i, \quad i \neq k \\ \end{cases} ∂xi​∂L​{−y^​k​yk​​⋅y^​k​(1−y^​k​)−yk​(1−y^​k​)y^​k​−1,ik−y^​k​yk​​⋅(−y^​k​⋅y^​i​)yk​⋅y^​i​y^​i​,ik​ 综上所述可以得到Softmax层的反向传播公式为 ∂ L ∂ x [ ∂ L ∂ x 1 … ∂ L ∂ x k … ∂ L ∂ x n ] [ ∂ L ∂ x 1 … ∂ L ∂ x k … ∂ L ∂ x n ] [ y ^ 1 … y ^ k − 1 … y ^ n ] \frac{\partial L}{\partial x} \begin{bmatrix} \frac{\partial{L}}{\partial{x_1}} \\ \dots \\ \frac{\partial{L}}{\partial{x_k}} \\ \dots \\ \frac{\partial{L}}{\partial{x_n}} \\ \end{bmatrix} \begin{bmatrix} \frac{\partial{L}}{\partial{x_1}} \\ \dots \\ \frac{\partial{L}}{\partial{x_k}} \\ \dots \\ \frac{\partial{L}}{\partial{x_n}} \\ \end{bmatrix} \begin{bmatrix} \hat{y}_1 \\ \dots \\ \hat{y}_k - 1 \\ \dots \\ \hat{y}_n \\ \end{bmatrix} ∂x∂L​ ​∂x1​∂L​…∂xk​∂L​…∂xn​∂L​​ ​ ​∂x1​∂L​…∂xk​∂L​…∂xn​∂L​​ ​ ​y^​1​…y^​k​−1…y^​n​​ ​ 由于 y k 1 y_k 1 yk​1 、其他 y i 0 y_i 0 yi​0 上式可以改写成 ∂ L ∂ x [ y ^ 1 … y ^ k − 1 … y ^ n ] [ y ^ 1 − y 1 … y ^ k − y k … y ^ n − y n ] y ^ − y \frac{\partial L}{\partial x} \begin{bmatrix} \hat{y}_1 \\ \dots \\ \hat{y}_k - 1 \\ \dots \\ \hat{y}_n \\ \end{bmatrix} \begin{bmatrix} \hat{y}_1 - y_1 \\ \dots \\ \hat{y}_k - y_k \\ \dots \\ \hat{y}_n - y_n \\ \end{bmatrix} \hat{y} - y ∂x∂L​ ​y^​1​…y^​k​−1…y^​n​​ ​ ​y^​1​−y1​…y^​k​−yk​…y^​n​−yn​​ ​y^​−y 代码层面 y ^ \hat{y} y^​ 即Softmax输出的概率分布 probs 、 y y y 即真实标签的one-hot分布考虑批次处理则结果还需除以 batch_size 。故可写出反向传播代码为 def backward(self):grad_in (self.probs - self.truth_onehot) / self.batch_sizereturn grad_in整体实现 综上所述Softmax层的整体代码实现为 class Softmax:def __init__(self, devicecpu):self.device devicedef forward(self, x):x_max torch.max(x, dim1, keepdimTrue) # 防止溢出减去最大值来增加数值稳定性x_exp torch.exp(x - x_max.values)self.probs x_exp / torch.sum(x_exp, dim1, keepdimTrue)return self.probsdef cross_entropy(self, predictions, truths):probs self.probs # Softmax得到的预测标签的概率分布# 设定一个最小值min_val防止log(0)min_val 1e-6probs torch.where(probs min_val, torch.full_like(probs, min_val), probs)# 计算交叉熵损失batch_size predictions.shape[0]truth_onehot torch.zeros(batch_size, probs.shape[1], deviceself.device)truth_onehot[torch.arange(batch_size), truths] 1loss -torch.mean(torch.sum(truth_onehot * torch.log(probs), dim1))self.batch_size batch_size # 批次大小self.truth_onehot truth_onehot # 真实标签的one-hot分布return lossdef backward(self):grad_in (self.probs - self.truth_onehot) / self.batch_sizereturn grad_in含单隐藏层的前馈神经网络MLP 由于上面已经实现了各组件的前向和反向传播方法故实现模型时只需要按顺序将它们组合在一起即可 class MLP:def __init__(self, input_size, hidden_size, output_size, devicecpu): 初始化单隐藏层网络 self.fc1 MyLinear(input_size, hidden_size, device) # 输入到隐藏层的线性变换self.relu ReLU() # 隐藏层激活函数self.fc2 MyLinear(hidden_size, output_size, device) # 隐藏层到输出层的线性变换self.softmax Softmax(device) # 输出层的 Softmax 激活def forward(self, x): 前向传播 x self.fc1.forward(x) x self.relu.forward(x) x self.fc2.forward(x) x self.softmax.forward(x) return xdef backward(self): 反向传播 grad self.softmax.backward()grad self.fc2.backward(grad)grad self.relu.backward(grad)grad self.fc1.backward(grad)def update(self, lr): 更新参数 self.fc1.update(lr)self.fc2.update(lr)训练评估 训练模型并记录训练过程中模型在训练集和测试集上的损失进行可视化 def train_and_evaluate(model, train_loader, test_loader, epochs, learning_rate):train_loss_history []test_loss_history []for epoch in range(epochs):total_loss 0for images, labels in train_loader:probs model.forward(images) # 前向传播loss model.softmax.cross_entropy(probs, labels) # 计算损失model.backward() # 反向传播model.update(learning_rate) # 更新参数total_loss loss.item()# 计算平均训练损失avg_train_loss total_loss / len(train_loader)train_loss_history.append(avg_train_loss)# 评估模型在测试集上的准确率accuracy, avg_test_loss evaluate(model, test_loader)test_loss_history.append(avg_test_loss)print(fEpoch {epoch1}/{epochs}, fTrain Loss: {avg_train_loss:.4f}, fTest Accuracy: {(accuracy * 100):.4f}%)# 绘制曲线plt.figure(figsize(10, 6))plt.plot(range(epochs), train_loss_history, labelTrain Loss)plt.plot(range(epochs), test_loss_history, labelTest Loss)plt.xlabel(Epoch)plt.ylabel(Loss)plt.title(Training and Test Loss Curve)plt.legend()plt.grid(True)plt.show()def evaluate(model, test_loader):correct 0total 0total_test_loss 0for images, labels in test_loader:probs model.forward(images)loss model.softmax.cross_entropy(probs, labels)total_test_loss loss.item()# 获取预测的类别_, predicted torch.max(probs, dim1) # 输出每行最大值的索引即预测的类别# 统计正确预测的数量correct (predicted labels).sum().item()total labels.size(0) accuracy correct / total # 计算准确率avg_test_loss total_test_loss / len(test_loader)return accuracy, avg_test_loss设定超参数开始训练 # 训练和评估 input_size 784 hidden_size 128 output_size 10 n_epochs 30 lr 1e-2 batch_size 64train_loader MyDataLoader(train_set, batch_size, shuffleTrue, devicemy_device) test_loader MyDataLoader(test_set, batch_size, shuffleFalse, devicemy_device)model MLP(input_size, hidden_size, output_size, my_device)train_and_evaluate(modelmodel,train_loadertrain_loader,test_loadertest_loader,epochsn_epochs,learning_ratelr )上述初始超参数下训练结果如下 可以看到最终准确率收敛在90%左右。为了提高准确率尝试减小批次大小、增加隐藏层神经元个数、调整学习率等部分实验结果如下 hidden_sizelrbatch_sizen_epochs收敛准确率±0.1%1280.01643090.3%2560.01643092.8%5120.01643093.7%5120.001323091.3%5120.02322095.1%5120.05162095.7%10240.011283093.8%10240.1162096.5% 本次实验中模型最好的分类准确度为96.5%左右 相关超参数为 hidden_size 1024 n_epochs 30 lr 0.1 batch_size 16可以看到由于本实验的数据集比较简单将学习率设置得比较高0.1模型仍能收敛。 代码文件移步 我的代码仓库
http://www.w-s-a.com/news/534916/

相关文章:

  • 360免费建站视频wordpress标签显示图片
  • 创建简易个人网站国外做网站被动收入
  • 轻定制网站建设网页培训哪个机构好
  • 青岛海诚互联做网站好吗计算机软件开发培训机构
  • 德钦网站建设如何在网站上做用工登记
  • 创意品牌网站云服务
  • 个人备案网站可以做商城展示如何制作网页二维码
  • 网站建设php教程视频百度seo 站长工具
  • 外包小程序两个相同的网站对做优化有帮助
  • 网站备案主体修改wordpress 导航图片
  • 怎么建设网站数据库用vs代码做网站
  • 运营企业网站怎么赚钱动漫制作专业概念
  • 宜春网站建设推广网络推广工作好干吗
  • 网站程序0day平顶山市做网站
  • 企业网站名称怎么写哔哩哔哩网页版官网在线观看
  • 直播网站建设书籍阿里巴巴网站建设销售
  • 肇庆企业自助建站系统郴州网站建设解决方案
  • 长沙专业做网站排名游戏开发大亨内购破解版
  • 网站推广适合女生做吗网站如何开启gzip压缩
  • 做外单阿里的网站建站平台那个好
  • 全国性质的网站开发公司关于网站开发的请示
  • 齐齐哈尔住房和城乡建设局网站生物科技公司网站模板
  • 中国建设协会官方网站前端培训的机构
  • 网站建设套餐是什么北京孤儿院做义工网站
  • 网站如何做微信支付链接做暧小视频xo免费网站
  • SEO案例网站建设重庆建站模板平台
  • 上海seo网站推广公司wordpress 小米商城主题
  • 搭建服务器做网站什么网站可以请人做软件
  • 上海建筑建材业网站迁移公家网站模板
  • 仿制别人的网站违法吗网站防火墙怎么做