ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【动手学深度学习】softmax回归从零开始实现的研究详情

【动手学深度学习】softmax回归从零开始实现的研究详情 目录1. 研究目的2. 研究准备3. 研究内容3.1 softmax回归的从零开始实现3.2 基础练习4. 研究体会1. 研究目的理解softmax回归的原理和基本实现方式学习如何从零开始实现softmax回归并了解其关键步骤通过简洁实现softmax回归掌握使用现有深度学习框架的能力探索softmax回归在分类问题中的应用并评估其性能。2. 研究准备根据GPU安装pytorch版本实现GPU运行研究代码配置环境用来运行 Python、Jupyter Notebook和相关库等相关库。3. 研究内容启动jupyter notebook使用新增的pytorch环境新建ipynb文件为了检查环境配置是否合理输入import torch以及torch.cuda.is_available()若返回TRUE则说明研究环境配置正确若返回False但可以正确导入torch则说明pytorch配置成功但研究运行是在CPU进行的结果如下3.1 softmax回归的从零开始实现1使用jupyter notebook新增的pytorch环境新建ipynb文件完成基本数据操作的研究代码与练习结果如下导入必要库和加载数据import torch from IPython import display from d2l import torch as d2l batch_size 256 train_iter, test_iter d2l.load_data_fashion_mnist(batch_size)初始化模型参数num_inputs 784 num_outputs 10 W torch.normal(0, 0.01, size(num_inputs, num_outputs), requires_gradTrue) b torch.zeros(num_outputs, requires_gradTrue)实现softmax运算X torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) X.sum(0, keepdimTrue), X.sum(1, keepdimTrue)def softmax(X): X_exp torch.exp(X) partition X_exp.sum(1, keepdimTrue) return X_exp / partition # 这里应用了广播机制 X torch.normal(0, 1, (2, 5)) X_prob softmax(X) X_prob, X_prob.sum(1)定义模型def net(X): return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) b)定义损失函数y torch.tensor([0, 2]) y_hat torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]]) y_hat[[0, 1], y]def cross_entropy(y_hat, y): return - torch.log(y_hat[range(len(y_hat)), y]) cross_entropy(y_hat, y)计算分类准确率def accuracy(y_hat, y): #save 计算预测正确的数量 if len(y_hat.shape) 1 and y_hat.shape[1] 1: y_hat y_hat.argmax(axis1) cmp y_hat.type(y.dtype) y return float(cmp.type(y.dtype).sum()) accuracy(y_hat, y) / len(y)def evaluate_accuracy(net, data_iter): #save 计算在指定数据集上模型的精度 if isinstance(net, torch.nn.Module): net.eval() # 将模型设置为评估模式 metric Accumulator(2) # 正确预测数、预测总数 with torch.no_grad(): for X, y in data_iter: metric.add(accuracy(net(X), y), y.numel()) return metric[0] / metric[1] class Accumulator: #save 在n个变量上累加 def init(self, n): self.data [0.0] * n def add(self, *args): self.data [a float(b) for a, b in zip(self.data, args)] def reset(self): self.data [0.0] * len(self.data) def getitem(self, idx): return self.data[idx] evaluate_accuracy(net, test_iter)训练模型def train_epoch_ch3(net, train_iter, loss, updater): #save 训练模型一个迭代周期定义见第3章 # 将模型设置为训练模式 if isinstance(net, torch.nn.Module): net.train() # 训练损失总和、训练准确度总和、样本数 metric Accumulator(3) for X, y in train_iter: # 计算梯度并更新参数 y_hat net(X) l loss(y_hat, y) if isinstance(updater, torch.optim.Optimizer): # 使用PyTorch内置的优化器和损失函数 updater.zero_grad() l.mean().backward() updater.step() else: # 使用定制的优化器和损失函数 l.sum().backward() updater(X.shape[0]) metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 返回训练损失和训练精度 return metric[0] / metric[2], metric[1] / metric[2] class Animator: #save 在动画中绘制数据 def init(self, xlabelNone, ylabelNone, legendNone, xlimNone, ylimNone, xscalelinear, yscalelinear, fmts(-, m--, g-., r:), nrows1, ncols1, figsize(3.5, 2.5)): # 增量地绘制多条线 if legend is None: legend [] d2l.use_svg_display() self.fig, self.axes d2l.plt.subplots(nrows, ncols, figsizefigsize) if nrows * ncols 1: self.axes [self.axes, ] # 使用lambda函数捕获参数 self.config_axes lambda: d2l.set_axes( self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend) self.X, self.Y, self.fmts None, None, fmts def add(self, x, y): # 向图表中添加多个数据点 if not hasattr(y, __len__): y [y] n len(y) if not hasattr(x, __len__): x [x] * n if not self.X: self.X [[] for _ in range(n)] if not self.Y: self.Y [[] for _ in range(n)] for i, (a, b) in enumerate(zip(x, y)): if a is not None and b is not None: self.X[i].append(a) self.Y[i].append(b) self.axes[0].cla() for x, y, fmt in zip(self.X, self.Y, self.fmts): self.axes[0].plot(x, y, fmt) self.config_axes() display.display(self.fig) display.clear_output(waitTrue) def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #save 训练模型定义见第3章 animator Animator(xlabelepoch, xlim[1, num_epochs], ylim[0.3, 0.9], legend[train loss, train acc, test acc]) for epoch in range(num_epochs): train_metrics train_epoch_ch3(net, train_iter, loss, updater) test_acc evaluate_accuracy(net, test_iter) animator.add(epoch 1, train_metrics (test_acc,)) train_loss, train_acc train_metrics assert train_loss 0.5, train_loss assert train_acc 1 and train_acc 0.7, train_acc assert test_acc 1 and test_acc 0.7, test_acc lr 0.1 def updater(batch_size): return d2l.sgd([W, b], lr, batch_size) num_epochs 10 train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)预测def predict_ch3(net, test_iter, n6): 预测标签定义见第3章 for X, y in test_iter: break trues d2l.get_fashion_mnist_labels(y) preds d2l.get_fashion_mnist_labels(net(X).argmax(axis1)) titles [true \n pred for true, pred in zip(trues, preds)] d2l.show_images( X[0:n].reshape((n, 28, 28)), 1, n, titlestitles[0:n]) predict_ch3(net, test_iter)3.2 基础练习1.本节直接实现了基于数学定义softmax运算的softmax函数。这可能会导致什么问题提示尝试计算(\exp(50))的大小。当计算exp(50)时可能会遇到数值溢出的问题。softmax函数的定义是通过对输入向量中的每个元素进行指数运算然后进行归一化使得所有元素的和为1。指数函数在输入较大时会迅速增长当输入超过一定阈值时指数函数的输出将变得非常大可能超出计算机所能表示的范围。在本节直接实现的softmax函数中如果输入向量中的某个元素较大例如50那么对应的指数运算结果将变得非常大导致数值溢出。这会导致计算结果不准确或无法表示。在实际应用中通常会使用数值稳定的方法来计算softmax函数以避免数值溢出的问题。常见的数值稳定方法是通过减去输入向量中的最大值来进行计算即softmax函数的输入进行平移。因此在直接实现基于数学定义的softmax函数时可能会遇到数值溢出的问题导致计算结果不准确或无法表示。import numpy as np def softmax(x): # 减去输入向量中的最大值以避免数值溢出 x - np.max(x) # 计算指数运算 exp_x np.exp(x) # 归一化计算softmax值 softmax_x exp_x / np.sum(exp_x) return softmax_x 计算 exp(50) 的 softmax 值 x np.array([50]) softmax_value softmax(x) #这段代码通过减去输入向量中的最大值在这种情况下就是50来避免数值溢出。 #然后使用NumPy的exp函数计算指数运算最后进行归一化得到softmax值。 #由于采取了数值稳定的计算方法即使输入为较大的数值例如50也能够正确计算softmax值。 #在这种情况下softmax值为1表示该元素在归一化后的向量中占比为100%。 print(softmax_value)结果2.本节中的函数cross_entropy是根据交叉熵损失函数的定义实现的。它可能有什么问题提示考虑对数的定义域。根据提示考虑对数的定义域。交叉熵损失函数在计算中通常会涉及对数运算而对数函数在定义域上有限制。对数函数的定义域是正实数即输入值必须大于零。在李沐老师的本节中如果交叉熵损失函数的计算结果中包含负数或零将会导致问题。这是因为对数函数在定义域之外没有定义尝试对负数或零进行对数运算将会导致错误或异常。特别是在计算softmax函数的交叉熵损失时可能会遇到这样的问题。当预测值与真实值之间存在较大的差异时交叉熵损失函数的计算结果可能会出现负数或零。这将导致对数运算无法进行进而影响整个损失函数的计算。为了解决这个问题通常会在交叉熵损失函数的计算中添加一个小的平滑项例如加上一个较小的常数如10的-8次方以确保避免出现负数或零。这被称为“平滑交叉熵”或“平滑对数损失”。因此如果在直接实现基于交叉熵损失函数的代码中没有处理对数函数定义域的限制可能会导致错误或异常特别是在涉及预测值与真实值之间差异较大的情况下。3.请想一个解决方案来解决上述两个问题。为了解决上述两个问题即数值溢出和对数函数定义域的限制可以采取以下解决方案数值溢出问题在计算softmax函数时通过减去输入向量中的最大值来避免数值溢出。这样做可以确保指数函数的输入在合理的范围内避免结果过大而导致数值溢出。这个方法在前面的回答中已经提到了。对数函数定义域问题在计算交叉熵损失函数时添加一个小的平滑项。可以在对数函数的输入上加上一个较小的常数例如如10的-8次方以确保避免出现负数或零。这样可以避免对数函数在定义域之外的值上计算确保损失函数的计算结果正确。下面是一个示例代码展示了如何结合这两个解决方案来计算softmax函数和交叉熵损失函数import numpy as np def softmax(x): x - np.max(x) exp_x np.exp(x) softmax_x exp_x / np.sum(exp_x) return softmax_x def cross_entropy(predicted, target): # 添加平滑项避免对数函数定义域的问题 smooth 1e-8 # 计算交叉熵损失 loss -np.sum(target * np.log(predicted smooth)) return loss 假设有一个预测向量和真实标签向量 predicted np.array([0.9, 0.1, 0.2]) target np.array([1, 0, 0]) 计算softmax函数的输出 softmax_output softmax(predicted) 计算交叉熵损失 loss cross_entropy(softmax_output, target) print(loss)结果在这段代码中我们在softmax函数中采用了减去最大值的方法以避免数值溢出。在交叉熵损失函数中添加了平滑项如10的-8次方以确保避免对数函数的定义域问题。通过结合这两个解决方案可以在计算softmax函数和交叉熵损失函数时避免数值溢出和对数函数定义域的限制从而得到准确的计算结果。4.返回概率最大的分类标签总是最优解吗例如医疗诊断场景下可以这样做吗在一些情况下返回概率最大的分类标签可以是一个合理的决策但并不总是最优解。特别是在医疗诊断等重要领域仅仅依靠概率最大的分类标签可能会带来一些问题。以下是一些原因不确定性分类模型的预测结果往往包含一定程度的不确定性。即使一个类别的概率最大但它的概率可能仍然相对较低。仅仅基于最大概率进行决策可能会忽略其他类别的潜在可能性。类别之间的差异在某些情况下不同类别之间的重要性或影响力可能会有所不同。概率最大的类别可能不是最重要的类别或者可能不是需要优先考虑的类别。风险和成本在医疗诊断等领域决策的结果可能会对患者的生命和健康产生直接影响。仅仅基于概率最大的分类标签进行决策可能会忽略可能的风险和成本导致不准确的结果或不适当的行动。因此在医疗诊断场景下通常需要更细致的分析和决策过程。除了分类模型的输出概率还需要考虑其他因素例如患者的病史、症状、实验室检查结果等。医疗决策往往是复杂的并需要由专业医生进行综合判断。尽管返回概率最大的分类标签在某些情况下可能是合理的但在医疗诊断等重要领域仅仅依靠概率最大的分类标签并不足够需要综合考虑其他因素并由专业人士进行决策。5.假设我们使用softmax回归来预测下一个单词可选取的单词数目过多可能会带来哪些问题?当可选取的单词数目过多时使用softmax回归来预测下一个单词可能会面临以下问题计算复杂度增加Softmax回归的计算复杂度与类别数目成正比。如果可选取的单词数目非常大那么计算softmax函数的指数运算和归一化操作将变得非常昂贵导致训练和推理的效率下降。内存消耗增加计算softmax函数所需的内存空间与类别数目成正比。当可选取的单词数目非常多时需要存储大量的权重参数和临时计算结果这可能导致内存消耗过大甚至超过可用的内存限制。数据稀疏性问题当可选取的单词数目非常多时每个单词的出现频率可能会变得非常稀疏。这会导致模型在训练过程中难以准确地估计每个单词的权重参数从而影响模型的性能和泛化能力。样本不平衡问题在大规模的单词集中不同单词的出现频率可能会有很大差异导致样本不平衡问题。某些常见的单词可能会有更多的训练样本而一些罕见的单词可能只有很少的训练样本。这会影响模型对于不常见单词的预测能力。为了解决上述问题可以采取一些技术手段例如降低可选取的单词数目可以通过限制词汇表的大小或使用更精确的单词选择方法减少可选取的单词数目从而降低计算和内存的负担。使用分层softmax或负采样等技术这些技术可以减少计算复杂度和内存消耗同时处理数据稀疏性和样本不平衡问题。使用更高级的模型除了softmax回归还可以尝试其他模型如深度神经网络、注意力机制等以提高模型的表达能力和性能。4. 研究体会通过这次研究我深入学习了softmax回归模型理解了它的原理和基本实现方式。我了解了softmax回归的背景和用途它在多类别分类问题中应用广泛学习了如何从零开始实现softmax回归并掌握了其中的关键步骤。在从零开始实现softmax回归时首先需要构建模型参数包括权重和偏差。通过使用Python和NumPy库能够方便地进行矩阵运算计算模型的预测结果。然后实现了softmax函数它将模型的原始输出转化为概率分布从而得到每个类别的概率预测。接下来定义了损失函数使用交叉熵损失来度量模型预测与真实标签之间的差异。通过最小化损失函数可以优化模型参数使预测更加准确。在优化过程中采用了梯度下降算法通过计算损失函数关于参数的梯度更新参数数值。通过简洁实现softmax回归我更加熟悉了深度学习框架的使用。可以通过几行代码完成模型的定义、数据的加载和训练过程。还学会了使用框架提供的工具来评估模型性能如计算准确率和绘制混淆矩阵。这使我能够更方便地对模型进行调试和优化以获得更好的分类结果。最后通过实验探索了softmax回归在分类问题中的应用并评估了其性能。使用了一些真实的数据集如MNIST手写数字数据集来进行实验。在实验中将数据集划分为训练集和测试集用训练集来训练模型然后用测试集来评估模型性能。在从零开始实现的实验中我对模型性能进行了一些调优比如调整学习率和迭代次数。观察到随着迭代次数的增加模型的训练损失逐渐下降同时在测试集上的准确率也在提升。这证明模型在一定程度上学习到了数据的规律并能够泛化到新的样本。
返回列表