为什么说神经网络可以逼近任意函数?
本文主要介紹神經(jīng)網(wǎng)絡(luò)萬(wàn)能逼近理論,并且通過(guò)PyTorch展示了兩個(gè)案例來(lái)說(shuō)明神經(jīng)網(wǎng)絡(luò)的函數(shù)逼近功能。
大多數(shù)人理解“函數(shù)”為高等代數(shù)中形如“f(x)=2x”的表達(dá)式,但是實(shí)際上,函數(shù)只是輸入到輸出的映射關(guān)系,其形式是多樣的。
拿個(gè)人衣服尺寸預(yù)測(cè)來(lái)說(shuō),我們用機(jī)器學(xué)習(xí)來(lái)實(shí)現(xiàn)這個(gè)功能,就是將個(gè)人身高、體重、年齡作為輸入,將衣服尺寸作為輸出,實(shí)現(xiàn)輸入-輸出映射。
具體來(lái)說(shuō),需要以下幾個(gè)步驟:
- 收集關(guān)鍵數(shù)據(jù)(大量人口的身高/體重/年齡,已經(jīng)對(duì)應(yīng)的實(shí)際服裝尺寸)。
- 訓(xùn)練模型來(lái)實(shí)現(xiàn)輸入-輸出的映射逼近。
- 對(duì)未知數(shù)據(jù)進(jìn)行預(yù)測(cè)來(lái)驗(yàn)證模型。
如果輸出是輸入特征的線性映射,那么模型的訓(xùn)練往往相對(duì)簡(jiǎn)單,只需要一個(gè)線性回歸就可以實(shí)現(xiàn);size = a*height + b*weight + c*age + d。
但是,通常假設(shè)輸出是輸入特征的線性映射是不夠合理和不完全準(zhǔn)確的。現(xiàn)實(shí)情況往往很復(fù)雜,存在一定的特例和例外。常見(jiàn)的問(wèn)題(字體識(shí)別、圖像分類等)顯然涉及到復(fù)雜的模式,需要從高維輸入特征中學(xué)習(xí)映射關(guān)系。
但是根據(jù)萬(wàn)能逼近理論,帶有單隱藏的人工神經(jīng)網(wǎng)絡(luò)就能夠逼近任意函數(shù),因此可以被用于解決復(fù)雜問(wèn)題。
人工神經(jīng)網(wǎng)絡(luò)
本文將只研究具有輸入層、單個(gè)隱藏層和輸出層的完全連接的神經(jīng)網(wǎng)絡(luò)。在服裝尺寸預(yù)測(cè)器的例子中,輸入層將有三個(gè)神經(jīng)元(身高、體重和年齡),而輸出層只有一個(gè)(預(yù)測(cè)尺寸)。在這兩者之間,有一個(gè)隱藏層,上面有一些神經(jīng)元(下圖中有5個(gè),但實(shí)際上可能更大一些,比如1024個(gè))。
網(wǎng)絡(luò)中的每個(gè)連接都有一些可調(diào)整的權(quán)重。訓(xùn)練意味著找到好的權(quán)重,使給定輸入集的預(yù)測(cè)大小與實(shí)際大小之間存在微小差異。
每個(gè)神經(jīng)元與下一層的每個(gè)神經(jīng)元相連。這些連接都有一定的權(quán)重。每個(gè)神經(jīng)元的值沿著每個(gè)連接傳遞,在那里它乘以權(quán)重。然后所有的神經(jīng)元都會(huì)向前反饋到輸出層,然后輸出一個(gè)結(jié)果。訓(xùn)練模型需要為所有連接找到合適的權(quán)重。萬(wàn)能逼近定理的核心主張是,在有足夠多的隱藏神經(jīng)元的情況下,存在著一組可以近似任何函數(shù)的連接權(quán)值,即使該函數(shù)不是像f(x)=x2那樣可以簡(jiǎn)潔地寫(xiě)下來(lái)的函數(shù)。即使是一個(gè)瘋狂的,復(fù)雜的函數(shù),比如把一個(gè)100x100像素的圖像作為輸入,輸出“狗”或“貓”的函數(shù)也被這個(gè)定理所覆蓋。
非線性關(guān)系
神經(jīng)網(wǎng)絡(luò)之所以能夠逼近任意函數(shù),關(guān)鍵在于將非線性關(guān)系函數(shù)整合到了網(wǎng)絡(luò)中。每層都可以設(shè)置激活函數(shù)實(shí)現(xiàn)非線性映射,換言之,人工神經(jīng)網(wǎng)絡(luò)不只是進(jìn)行線性映射計(jì)算。常見(jiàn)的非線性激活函數(shù)有 ReLU, Tanh, Sigmoid等。
ReLU是一個(gè)簡(jiǎn)單的分段線性函數(shù)-計(jì)算消耗小。另外兩個(gè)都涉及到指數(shù)運(yùn)算,因此計(jì)算成本更高
為了展示人工神經(jīng)網(wǎng)絡(luò)的萬(wàn)能逼近的能力,接下來(lái)通過(guò)PyTorch實(shí)現(xiàn)兩個(gè)案例。
案例一:任意散點(diǎn)曲線擬合
神經(jīng)網(wǎng)絡(luò)可能面臨的最基本的情況之一就是學(xué)習(xí)兩個(gè)變量之間的映射關(guān)系。例如,假設(shè)x值表示時(shí)間,y坐標(biāo)表示某條街道上的交通量。在一天中的不同時(shí)間點(diǎn)都會(huì)出現(xiàn)高峰和低谷,因此這不是一種線性關(guān)系。
下面的代碼首先生成符合正態(tài)分布的隨機(jī)點(diǎn),然后訓(xùn)練一個(gè)網(wǎng)絡(luò),該網(wǎng)絡(luò)將x坐標(biāo)作為輸入,y坐標(biāo)作為輸出。有關(guān)每個(gè)步驟的詳細(xì)信息,請(qǐng)參見(jiàn)代碼注釋:
import torch import plotly.graph_objects as go import numpy as np# Batch Size, Input Neurons, Hidden Neurons, Output Neurons N, D_in, H, D_out = 16, 1, 1024, 1# Create random Tensors to hold inputs and outputs x = torch.randn(N, D_in) y = torch.randn(N, D_out)# Use the nn package to define our model # Linear (Input -> Hidden), ReLU (Non-linearity), Linear (Hidden-> Output) model = torch.nn.Sequential(torch.nn.Linear(D_in, H),torch.nn.ReLU(),torch.nn.Linear(H, D_out), )# Define the loss function: Mean Squared Error # The sum of the squares of the differences between prediction and ground truth loss_fn = torch.nn.MSELoss(reduction='sum')# The optimizer does a lot of the work of actually calculating gradients and # applying backpropagation through the network to update weights learning_rate = 1e-4 optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)# Perform 30000 training steps for t in range(30000):# Forward pass: compute predicted y by passing x to the model.y_pred = model(x)# Compute loss and print it periodicallyloss = loss_fn(y_pred, y)if t % 100 == 0:print(t, loss.item())# Update the network weights using gradient of the lossoptimizer.zero_grad()loss.backward()optimizer.step()# Draw the original random points as a scatter plot fig = go.Figure() fig.add_trace(go.Scatter(x=x.flatten().numpy(), y=y.flatten().numpy(), mode="markers"))# Generate predictions for evenly spaced x-values between minx and maxx minx = 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)# Draw the predicted functions as a line graph fig.add_trace(go.Scatter(x=c.flatten().numpy(), y=d.flatten().detach().numpy(), mode="lines")) fig.show()- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
請(qǐng)注意右邊的兩點(diǎn),即模型沒(méi)有完全擬合。我們可以通過(guò)運(yùn)行更多的訓(xùn)練步驟或增加隱藏神經(jīng)元的數(shù)量來(lái)解決這個(gè)問(wèn)題。
案例二:二值分類
函數(shù)不一定是在代數(shù)中看到的那種“一個(gè)數(shù)進(jìn)去,另一個(gè)數(shù)出來(lái)”的函數(shù)。現(xiàn)在讓我們嘗試一個(gè)二進(jìn)制分類任務(wù)。數(shù)據(jù)點(diǎn)有兩個(gè)特征,可以分為兩個(gè)標(biāo)簽中的一個(gè)。也許這兩個(gè)特征是經(jīng)緯度坐標(biāo),而標(biāo)簽是環(huán)境污染物的存在。或者,這些特征可能是學(xué)生的數(shù)學(xué)和閱讀測(cè)試成績(jī),并且標(biāo)簽對(duì)應(yīng)于他們是右撇子還是左撇子。重要的是模型必須實(shí)現(xiàn)兩個(gè)輸入到一個(gè)輸出(0或1)的映射。
下面的代碼與前面的代碼非常相似。唯一的差異是輸入層現(xiàn)在有兩個(gè)神經(jīng)元,輸出層之后是一個(gè)Sigmoid激活,它將所有輸出壓縮到范圍(0,1)。
import torch import plotly.express as px import pandas as pd# Batch Size, Input Neurons, Hidden Neurons, Output Neurons N, D_in, H, D_out = 128, 2, 1024, 1# Create random Tensors to hold inputs and outputs x = torch.rand(N, D_in) y = torch.randint(0, 2, (N, D_out))# Plot randomly generated points and color by label df = 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()# define model: Linear (Input->Hidden), ReLU, Linear (Hidden->Output), Sigmoid model = torch.nn.Sequential(torch.nn.Linear(D_in, H),torch.nn.ReLU(),torch.nn.Linear(H, D_out),torch.nn.Sigmoid() )# define loss function: Binary Cross Entropy Loss (good for binary classification tasks) loss_fn = torch.nn.BCELoss()learning_rate = 0.002 optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)# Store losses over time ts, losses = ([], [])# run training steps for t in range(60000):y_pred = model(x)loss = loss_fn(y_pred.float(), y.float())if t % 100 == 0:ts.append(t)losses.append(loss.data.numpy())optimizer.zero_grad()loss.backward()optimizer.step()# generate a bunch of random points to cover the sample space, then call model c = torch.rand(32000, D_in) d = model(c)# store random data and predicted classifications in a DataFrame and plot with Plotly Express df2 = 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()# plot the loss as a function of training step fig3 = px.scatter(x=ts, y=losses) fig3.show()- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
在單位正方形中隨機(jī)均勻生成的點(diǎn),隨機(jī)指定給標(biāo)簽0(青色)和標(biāo)簽1(粉紅色)。
首先,在單位正方形內(nèi)隨機(jī)均勻生成數(shù)據(jù)點(diǎn),并且隨機(jī)指點(diǎn)每個(gè)數(shù)據(jù)點(diǎn)的標(biāo)簽為0/1。從圖中可以看出,顯然不存在線性關(guān)系。本案例的目的在于訓(xùn)練模型使其通過(guò)坐標(biāo)判斷標(biāo)簽。
模型分類結(jié)果
過(guò)擬合
以上兩個(gè)案例似乎都給出了很可觀的結(jié)果,但是這是不是我們真正想要的呢?
值得注意的是,這兩個(gè)案例都存在過(guò)擬合的現(xiàn)象。過(guò)擬合表現(xiàn)為模型在訓(xùn)練數(shù)據(jù)集表現(xiàn)優(yōu)秀,但是在未知數(shù)據(jù)集表現(xiàn)不足。
在案例一中,假設(shè)其中一個(gè)點(diǎn)是由于錯(cuò)誤的數(shù)據(jù)收集而導(dǎo)致的異常值。考慮到要學(xué)習(xí)的訓(xùn)練數(shù)據(jù)量如此之少,模型對(duì)這些數(shù)據(jù)的擬合度過(guò)高,只看到了一個(gè)信號(hào),而實(shí)際上只是噪聲。一方面,令人印象深刻的是,模型能夠?qū)W習(xí)一個(gè)考慮到這個(gè)異常值的函數(shù)。另一方面,當(dāng)將此模型應(yīng)用于真實(shí)世界的數(shù)據(jù)時(shí),這可能會(huì)導(dǎo)致不良結(jié)果,在該點(diǎn)附近產(chǎn)生錯(cuò)誤的預(yù)測(cè)。
在案例二中,模型學(xué)習(xí)了一個(gè)漂亮的分類預(yù)測(cè)。但是,請(qǐng)注意最靠近右下角的藍(lán)綠色點(diǎn)。盡管這是唯一的一點(diǎn),它導(dǎo)致模型將整個(gè)右下角標(biāo)記為青色。僅僅是一些錯(cuò)誤的數(shù)據(jù)點(diǎn)就可能嚴(yán)重扭曲模型。當(dāng)我們嘗試將模型應(yīng)用于測(cè)試數(shù)據(jù)時(shí),它的工作效果可能比預(yù)期的差得多。
為了避免過(guò)度擬合,重要的是要有大量的訓(xùn)練數(shù)據(jù)來(lái)代表模型預(yù)期面對(duì)的樣本。如果你正在建立一個(gè)工具來(lái)預(yù)測(cè)普通人群的衣服尺寸,不要只從你大學(xué)朋友那里收集訓(xùn)練數(shù)據(jù)。此外,還有一些先進(jìn)的技術(shù)可以別用于幫助減少過(guò)擬合的發(fā)生(例如:權(quán)重下降 weight decay)。
結(jié)語(yǔ)
總之,神經(jīng)網(wǎng)絡(luò)是強(qiáng)大的機(jī)器學(xué)習(xí)工具,因?yàn)樗鼈?#xff08;理論上)能夠?qū)W習(xí)任何函數(shù)。然而,這并不能保證你很容易找到一個(gè)給定問(wèn)題的最優(yōu)權(quán)重!實(shí)際上,在合理的時(shí)間內(nèi)訓(xùn)練一個(gè)精確的模型取決于許多因素,例如優(yōu)化器、模型體系結(jié)構(gòu)、數(shù)據(jù)質(zhì)量等等。特別是,深度學(xué)習(xí)涉及具有多個(gè)隱藏層的神經(jīng)網(wǎng)絡(luò),它們非常擅長(zhǎng)學(xué)習(xí)某些困難的任務(wù)。
作者:Thomas Hikaru Clark
deephub翻譯組 Oliver Lee
來(lái)源:https://blog.csdn.net/m0_46510245/article/details/107503883
總結(jié)
以上是生活随笔為你收集整理的为什么说神经网络可以逼近任意函数?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于OpenCV的简单人脸识别系统
- 下一篇: 部队打胜仗的16字标语有哪些?