当前位置: 首页 > 科技观察

为什么说神经网络可以逼近任意函数呢?

时间:2023-03-12 16:31:02 科技观察

本文主要介绍了神经网络的万能逼近理论,并通过PyTorch展示了两个案例来说明神经网络的函数逼近功能。大多数人将“函数”理解为高等代数中“f(x)=2x”形式的表达式,但实际上,函数只是一种从输入到输出的映射关系,其形式多种多样。以个人服装尺码预测为例,我们使用机器学习来实现这个功能,即以个人身高、体重、年龄作为输入,以服装尺码作为输出,实现输入输出映射。具体需要以下步骤:收集关键数据(大量人群的身高/体重/年龄,对应的实际衣服尺码)。训练模型以近似输入输出映射。对看不见的数据进行预测以验证模型。如果输出是输入特征的线性映射,那么模型的训练往往比较简单,只需要一次线性回归就可以实现;尺码=a*身高+b*体重+c*年龄+d。然而,假设输出是输入特征的线性映射通常是不合理且不完全准确的。现实往往是复杂的,有一定的特殊情况和例外。常见问题(字体识别、图像分类等)显然涉及复杂的模式,需要从高维输入特征中学习映射。但根据万能逼近理论,单隐层的人工神经网络可以逼近任何函数,因此可以用来解决复杂的问题。人工神经网络本文将只研究具有输入层、单个隐藏层和输出层的全连接神经网络。在衣服尺寸预测器的示例中,输入层将具有三个神经元(身高、体重和年龄),而输出层将只有一个(预测尺寸)。在它们之间,有一个隐藏层,上面有一些神经元(下图中有5个,但实际上可能更大,比如1024)。网络中的每个连接都有一些可调整的权重。训练意味着找到好的权重,使得给定输入集的预测大小和实际大小之间存在微小差异。每个神经元都连接到下一层中的每个神经元。这些连接具有一定的权重。每个神经元的值沿着每个连接传递,并乘以权重。然后所有神经元前馈到输出层并输出结果。训练模型需要为所有连接找到合适的权重。万能逼近定理的中心主张是,给定足够多的隐藏神经元,存在一组可以逼近任何函数的连接权重,即使该函数不像f(x)=x2那样简洁地写成函数。即使是像将100x100像素图像作为输入并输出“狗”或“猫”这样疯狂、复杂的函数,也包含在该定理中。非线性关系神经网络能够逼近任意函数的关键在于将非线性关系函数集成到网络中。每层都可以设置激活函数来实现非线性映射。也就是说,人工神经网络不只是进行线性映射计算。常见的非线性激活函数包括ReLU、Tanh、Sigmoid等。ReLU是一种简单的分段线性函数——计算成本低。另外两个涉及指数运算,因此计算成本较高。为了展示人工神经网络的通用逼近能力,通过PyTorch实现了两个案例。案例1:任意散点曲线拟合神经网络可能面临的最基本情况之一是学习两个变量之间的映射。例如,假设x值表示时间,y坐标表示某条街道上的交通量。一天中的不同时间有高峰和低谷,所以不是线性关系。下面的代码首先生成符合正态分布的随机点,然后训练一个以x坐标为输入,y坐标为输出的网络。有关每个步骤的详细信息,请参见代码注释:importtorchimportplotly.graph_objectsasgoimportnumpyasnp#BatchSize,InputNeurons,HiddenNeurons,OutputNeuronsN,D_in,H,D_out=16,1,1024,1#CreaterandomTensorstoholdinputsandoutputsx=torch.randn(N,D_in)y=torch.randn(N,D_out)#Usthennpackagetodefineourmodel#Linear(Input->Hidden),ReLU(非线性),Linear(Hidden->Output)model=torch.nn.Sequential(torch.nn.Linear(D_in,H),torch.nn.ReLU(),torch.nn.Linear(H,D_out),)#Definethelossfunction:MeanSquaredError#Thesumofthesquaredsofthedifferencesbetweenpredictionandgroundtruthloss_fn=torch.nn.MSELoss(reduction='sum')#优化器做了很多实际计算梯度的工作并且#通过网络应用反向传播来更新dateweightslearning4imptrate=1=torch.optim.Adam(model.parameters(),lr=learning_rate)#Perform30000trainingstepsfortinrange(30000):#Forwardpass:computepredictedybypassingxtothemodel.y_pred=模型(x)#Computelossandprintitperiodicallyloss=loss_fn(y_pred,y)ift%100==0:优先级nt(t,loss.item())#Updatethenetworkweightsusinggradientofthelossoptimizer.zero_grad()loss.backward()optimizer.step()#Drawtheoriginalrandompointsasascatterplotfig=go.Figure()fig.add_trace(go.Scatter(x=x.flatten().numpy(),y=y.flatten().numpy(),mode="markers"))#Generatepredictionsforevenlyspacedx-valuesbetweenminxandmaxxminx=min(list(x.numpy()))maxx=max(list(x.numpy()))c=torch.from_numpy(np.linspace(minx,maxx,num=640)).reshape(-1,1).float()d=model(c)#Drawthepredictedfunctionsasalinegraphfig.add_trace(go.Scatter(x=c.flatten().numpy(),y=d.flatten().detach().numpy(),mode="lines"))fig.show()请注意右边两点,即也就是说,模型没有完全拟合我们可以通过运行更多的训练步骤或增加隐藏神经元的数量来解决这个问题。情况2:二分类函数不一定是代数中看到的那种“一个数进去,另一个数出来”的函数。现在让我们尝试一个二元分类任务。一个数据点有两个特征,可以归类到两个标签之一。也许这两个特征是经纬度坐标,标签是环境污染物的存在。或者,这些特征可能是学生的数学和阅读测试成绩,标签对应于他们是右撇子还是左撇子。重要的是模型必须实现两个输入到一个输出(0或1)的映射。下面的代码与前面的代码非常相似。唯一的区别是输入层现在有两个神经元,输出层后面跟着一个sigmoid激活,它将所有输出压缩到范围(0,1)。importtorchimportplotly.expressaspximportpandasaspd#BatchSize,InputNeurons,HiddenNeurons,OutputNeuronsN,D_in,H,D_out=128,2,1024,1#CreaterandomTensorstoholdinputsandoutputsx=torch.rand(N,D_in)y=torch.randint(0,2,(N,D_out)))#Plotrandomlygeneratedpointsandcolorbylabeldf=pd.DataFrame({"x":x[:,0].flatten(),"y":x[:,1].flatten(),"class":y.flatten()})fig=px.scatter(df,x="x",y="y",color="class",color_continuous_scale="tealrose")fig.show()#definemodel:线性(输入->隐藏),ReLU,线性(隐藏->输出),Sigmoidmodel=torch.nn.Sequential(torch.nn.Linear(D_in,H),torch.nn.ReLU(),torch.nn.Linear(H,D_out),torch.nn.Sigmoid())#definelossfunction:BinaryCrossEntropyLoss(goodforbinaryclassificationtasks)loss_fn=torch.nn.BCELoss()learning_rate=0.002optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)#Storelossesovertimets,losses=([],[])#runtrainingstepsfortinrange(60000):y_pred=模型(x)loss=loss_fn(y_pred.float(),y.float())ift%100==0:ts.append(t)losses.append(loss.data.numpy())optimizer.zero_grad()loss.backward()optimizer.step()#generateabunchofrandompointstocoverthesamplespace,thencallmodelc=torch.rand(32000,D_in)d=model(c)#storerandomdataandpredictedclassificationsinaDataFrameandplotwithPlotlyExpressdf2=pd.DataFrame({"x":c[:,0].flatten(),"y":c[:,1].flatten(),"class":d.flatten().detach().numpy()})fig2=px.scatter(df2,x="x",y="y",color="class",color_continuous_scale="tealrose")fig2.show()#plotthelossasafunctionoftrainingstepfig3=px.scatter(x=ts,y=losses)fig3.show()在单位正方形内随机均匀生成点,随机分配给label0(青色)和label1(粉色)首先,在单位正方形内随机均匀生成数据点,给每个数据点随机分配label0/1。从图中可以看出,显然没有线性关系。本案例的目的是训练模型通过坐标判断标签。模型分类结果过拟合上面两种情况看似给出了非常可观的结果,但这是我们真正想要的吗?值得注意的是,这两种情况都存在过拟合。过拟合是指模型在训练数据集中表现良好,但在未知数据集中表现不佳。在第一种情况下,假设其中一个点是由于错误的数据收集而导致的异常值。如果要从如此少量的训练数据中学习,模型会过度拟合数据并在信号实际上只是噪声时看到信号。一方面,令人印象深刻的是,该模型能够学习一个将这个异常值考虑在内的函数。另一方面,当将此模型应用于现实世界的数据时,这可能会导致糟糕的结果,从而在该点周围产生错误的预测。在第二种情况下,模型学习了一个很好的分类预测。但是,请注意最靠近右下角的青色点。尽管这是唯一的一点,但它会导致模型将整个右下角标记为青色。仅仅几个错误的数据点就会严重扭曲模型。当我们尝试将模型应用于测试数据时,它的效果可能比预期的要差得多。为了避免过度拟合,重要的是要有大量的训练数据来代表模型预期要面对的样本。如果你正在构建一个工具来预测一般人群的衣服尺码,不要只是从你的大学朋友那里收集训练数据。此外,还有一些高级技术可用于帮助减少过度拟合的发生(例如:权重衰减)。总之,神经网络是强大的机器学习工具,因为它们(理论上)能够学习任何功能。但是,这并不能保证您可以轻松找到给定问题的最佳权重!实际上,在合理的时间内训练出准确的模型取决于许多因素,例如优化器、模型架构、数据质量等。特别是深度学习涉及具有多个隐藏层的神经网络,非常擅长学习某些困难的任务。