前言
思索了很久到底要不要出深度学习内容,毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新,很多坑都没有填满,而且现在深度学习的文章和学习课程都十分的多,我考虑了很久决定还是得出神经网络系列文章,不然如果以后数学建模竞赛或者是其他更优化模型如果用上了神经网络(比如利用LSTM进行时间序列模型预测),那么就更好向大家解释并且阐述原理了。但是深度学习的内容不是那么好掌握的,包含大量的数学理论知识以及大量的计算公式原理需要推理。且如果不进行实际操作很难够理解我们写的代码究极在神经网络计算框架中代表什么作用。不过我会尽可能将知识简化,转换为我们比较熟悉的内容,我将尽力让大家了解并熟悉神经网络框架,保证能够理解通畅以及推演顺利的条件之下,尽量不使用过多的数学公式和专业理论知识。以一篇文章快速了解并实现该算法,以效率最高的方式熟练这些知识。
现在很多竞赛虽然没有限定使用算法框架,但是更多获奖的队伍都使用到了深度学习算法,传统机器学习算法日渐式微。比如2022美国大学生数学建模C题,参数队伍使用到了深度学习网络的队伍,获奖比例都非常高,现在人工智能比赛和数据挖掘比赛都相继增多,对神经网络知识需求也日渐增多,因此十分有必要掌握各类神经网络算法。
博主专注建模四年,参与过大大小小数十来次数学建模,理解各类模型原理以及每种模型的建模流程和各类题目分析方法。此专栏的目的就是为了让零基础快速使用各类数学模型、机器学习和深度学习以及代码,每一篇文章都包含实战项目以及可运行代码。博主紧跟各类数模比赛,每场数模竞赛博主都会将最新的思路和代码写进此专栏以及详细思路和完全代码。希望有需求的小伙伴不要错过笔者精心打造的专栏。
输出层
神经网络在解决不同类型的问题时,使用不同的激活函数来适应任务的需求。对于分类问题和回归问题,我们选择了不同的策略来构建网络。
首先,让我们考虑分类问题。在分类问题中,我们常常面临将数据分成不同类别的挑战。对于二分类问题,这意味着我们需要将数据分为两个可能的类别。在这种情况下,我们选择使用 Sigmoid 函数作为输出层的激活函数。Sigmoid 函数能够将输出值映射到 0 到 1 之间,这使得我们可以将输出视为某个类别的概率。
而在处理多分类问题时,我们的目标是将数据分成多个可能的类别。这时,我们使用 Softmax 函数作为输出层的激活函数。Softmax 函数可以将每个类别的原始分数转换为表示各类别概率的形式。这使得我们能够确定哪个类别最有可能是给定输入的正确类别。
而在回归问题中,我们关心的是预测连续数值的输出,而不是离散的类别。在这种情况下,我们通常不使用激活函数,因为我们希望网络的输出可以取任意实数值,以便与问题的需求相匹配。
综上所述,神经网络在不同问题类型中的激活函数选择是根据问题的性质和目标而定的。通过选择适当的激活函数,我们能够更好地让网络适应不同类型的任务。
一、工作过程
为了方便理解,我们仍然以一个实际案例来进行,采用Softmax来进行多分类。
一个多分类问题,C=4.线性分类器模型最后的输出层包含了四个输出值,分别是:
经过Softmax处理后,数值转化为如下所示概率:
很明显根据计算的概率我们很容易发现S2=0.8390对应的概率最大。Softmax将连续数值转化为相对概率,更利于我们理解。当然如果我们拿到的是[1000,1001,1002],我们会得到inf,如果是[-1000,-999,-1000]还是不行,也会得到-inf。
实际应用中,需要对V进行一些数值处理:即V中的每个元素减去V中的最大值。
def _softmax(x):
c=np.max(x)
exp_x = np.exp(x-c)
return exp_x/np.sum(exp_x)
scores = np.array([123,456,789])
p = _softmax(scores)
print(p)
以数值的形式计算可能有些抽象,那么我们以图片分类来举例:
假设我们把猫分为类1,狗为类2,小鸡为类3,如果不属于以上任何一类就分到“其他”,从左到右第一个图是一只小鸡,所以我们将它归为类3,以此类推:
假设我们输入一张猫的图片,其对应的真实标签是0100(类别已经转换成one-hot编码形式)。
真值y为其中,是1,其余的都是0,经过Softmax计算之后得到的是预测值y_predict,假设预测值为,它是一个包括总和为1的概率的向量,这样的话神经网络计算得到的结果告诉我们猫的概率值分配到20%。般来说,神经网络是将输出值最大的神经元所对应的类别作为识别结果,而且即使使用Softmax函数也只会改变值的大小而不能改变神经元的位置,另外指数函数的运算也需要一定的计算机运算量,因此可以考虑在多分类问题中省去Softmax函数。
二、输出层的神经元个数
输出层中神经元的数量应当根据解决问题的要求来选择。在处理不同问题时,输出层神经元的个数需要进行相应的设置。特别是在分类问题中,我们需要根据类别的数量来决定输出层的神经元数目。以 MNIST(手写数字识别)为例,这是一个将手写数字分为0到9共10个类别的问题,因此在输出层我们会设置10个神经元,每个神经元对应一个数字类别。
基于MNIST数据集的前向传播
MNIST(Modified National Institute of Standards and Technology)数据集是一个广泛应用于计算机视觉领域的经典数据集,用于手写数字识别任务。该数据集由美国国家标准与技术研究所(NIST)创建,并经过修改以便在机器学习研究中使用。
MNIST数据集包含了一系列的手写数字图像,涵盖了0到9的数字。每张图像的尺寸为28x28像素,以灰度图像的形式呈现。数据集共分为两部分:训练集和测试集。
训练集: 训练集包含了60,000张手写数字图像,每张图像都有对应的标签,指明该图像所表示的数字。这些图像和标签被广泛用于训练机器学习模型,特别是用于构建手写数字识别模型。
测试集: 测试集包含了10,000张手写数字图像,同样也带有对应的标签。这些图像用于评估已经训练好的模型在未见过数据上的性能。通过测试集的结果,可以了解模型的泛化能力和准确性。
通过torch直接下载即可:
#MNIST dataset
train_dataset = dsets.MNIST(root = '/ml/pymnist', #选择数据的根目录
train = True, #选择训练集
transform = None, #不考#MNIST dataset
train_dataset = dsets.MNIST(root = '/ml/pymnist', #选择数据的根目录
train = True, #选择训练集
transform = None, #不考虑使用任何数据预处理
download = True #从网络上下载图片
)
test_dataset = dsets.MNIST(root = '/ml/pymnist',#选择数据的根目录
train = False,#选择测试集
transform = None, #不考虑使用任何数据预处理
download = True #从网络上下载图片
)虑使用任何数据预处理
download = True #从网络上下载图片
)
test_dataset = dsets.MNIST(root = '/ml/pymnist',#选择数据的根目录
train = False,#选择测试集
transform = None, #不考虑使用任何数据预处理
download = True #从网络上下载图片
)
首先我们要初始化网络init_network函数,需要设置weight_scale变量用于控制随机权重,统一将bias设置为1.
def init_network():
network={}
weight_scale=1e-3
network['W1']=np.random.randn(784,50)*weight_scale
network['b1']=np.ones(50)
network['W2']=np.random.randn(50,100)*weight_scale
network['b2']=np.ones(100)
network['W3']=np.random.randn(100,10)*weight_scale
network['b3']=np.ones(10)
return network
之后是进行前向传播过程,这里采用ReLU函数:
def _relu(x):
return np.maximum(0,x)
def forward(network,x):
w1,w2,w3 = network['W1'],network['W2'],network['W3']
b1,b2,b3 = network['b1'],network['b2'],network['b3']
a1 = x.dot(w1) + b1
z1 = _relu(a1)
a2 = z1.dot(w2) + b2
z2 = _relu(a2)
a3 = z2.dot(w3) + b3
y = a3
return y
最后我们计算得到最终结果:
network = init_network()
accuracy_cnt = 0
x = test_dataset.test_data.numpy().reshape(-1,28*28)
labels = test_dataset.test_labels.numpy() #tensor转numpy
for i in range((len(x))):
y = forward(network,x[i])
p = np.argmax(y) #获取概率最高的元素的索引
if p == labels[i]:
accuracy_cnt += 1
print("Accuracy:"+ str(float(accuracy_cnt)/len(x)*100)+'%')
因为是初始权重,故10分类猜对的概率为10%正常不过,因为我们只是前向传播,没有反向传播,故所有的bias权重都不是最优的。那么下一章我们将为反向传播做好准备,详细讲述一下损失函数的作用,在我之前的文章基本把所有的损失函数都讲了一遍,十分详细完善,推荐大家有能力阅读一下:
损失函数(Loss Function)一文详解-分类问题常见损失函数Python代码实现+计算原理解析
下篇文章着重将讲述实际案例中损失函数的使用以及功能。