chap4 simple neural network

全连接神经网络

问题描述

利用numpypytorch搭建全连接神经网络。使用numpy实现此练习需要自己手动求导,而pytorch具有自动求导机制。

我们首先先手动算一下反向传播的过程,使用的模型和初始化权重、偏差和训练用的输入和输出值如下:
在这里插入图片描述
我们看一下正向过程:计算出每个隐藏神经元的输入,通过激活函数(用Sigmoid函数)转换为下一层的输入,直到达到输出层计算最终输出:
先来计算隐藏层h_1的输入,
z h 1 = ω 1 x 1 + ω 2 x 2 + 1 = 1 ∗ 1 + ( − 2 ) ∗ ( − 1 ) + 1 = 4 z_{h_1}=\omega_1 x_1+\omega_2 x_2 + 1=1*1+(-2)*(-1)+1=4 zh1=ω1x1+ω2x2+1=11+(2)(1)+1=4
然后用激活函数激活,得到 h 1 h_1 h1的输出
a h 1 = σ ( z h 1 ) = 1 1 + e − z h 1 = 1 1 + e − 4 = 0.98201379 a_{h_1}=\sigma(z_{h_1})=\frac{1}{1+e^{-z_{h_1}}}=\frac{1}{1+e^{-4}}=0.98201379 ah1=σ(zh1)=1+ezh11=1+e41=0.98201379
同理有
z h 2 = ω 3 x 1 + ω 4 x 2 + 1 = − 1 ∗ 1 + 1 ∗ ( − 1 ) + 1 = − 1 z_{h_2}=\omega_3 x_1+\omega_4 x_2 + 1=-1*1+1*(-1)+1=-1 zh2=ω3x1+ω4x2+1=11+1(1)+1=1
可得 a h 2 = σ ( z h 2 ) = 0.26894142 a_{h_2}=\sigma(z_{h_2})=0.26894142 ah2=σ(zh2)=0.26894142
这两个输出作为下一层的输入,接下来计算输出层 o 1 o_1 o1
z o 1 = ω 5 ∗ a h 1 + ω 6 ∗ a h 2 + 1 = 2 ∗ 0.98201379 + ( − 2 ) ∗ 0.26894142 + 1 = 2.42614474 z_{o_1}=\omega_5*a_{h_1}+\omega_6*a_{h_2}+1=2*0.98201379+(-2)*0.26894142+1=2.42614474 zo1=ω5ah1+ω6ah2+1=20.98201379+(2)0.26894142+1=2.42614474
a o 1 = σ ( z o 1 ) = 1 1 + e − z o 1 = 1 1 + e − 2.42614474 = 0.91879937 a_{o_1}=\sigma(z_{o_1})=\frac{1}{1+e^{-z_{o_1}}}=\frac{1}{1+e^{-2.42614474}}=0.91879937 ao1=σ(zo1)=1+ezo11=1+e2.426144741=0.91879937
同理,得到输出层 o 2 o_2 o2的输出
z o 2 = ω 7 ∗ a h 1 + ω 8 ∗ a h 2 + 1 = − 2 ∗ 0.98201379 + ( − 1 ) ∗ 0.26894142 + 1 = − 1.23296900 z_{o_2}=\omega_7*a_{h_1}+\omega_8*a_{h_2}+1=-2*0.98201379+(-1)*0.26894142+1=-1.23296900 zo2=ω7ah1+ω8ah2+1=20.98201379+(1)0.26894142+1=1.23296900
a o 2 = σ ( z o 2 ) = 1 1 + e − z o 2 = 1 1 + e − 1.23296900 = 0.22566220 a_{o_2}=\sigma(z_{o_2})=\frac{1}{1+e^{-z_{o_2}}}=\frac{1}{1+e^{-1.23296900}}=0.22566220 ao2=σ(zo2)=1+ezo21=1+e1.232969001=0.22566220
可以看到,初始参数上的输出和目标值0.010.99有不小的距离,下面计算一下总误差

使用均方误差来计算总误差:
E total  = 1 2 ∑ ( y ^ − y ) 2 E_{\text {total }}=\frac{1}{2} \sum(\hat{y}-y)^2 Etotal =21(y^y)2
其中 y y y是输出层的实际输出, y ^ \hat y y^是期望输出,比如对于 o 1 o_1 o1神经元,有误差:
E o 1 = 1 2 ( y o 1 ^ − y o 1 ) 2 = 1 2 ( 0.01 − 0.91879937 ) 2 = 0.41295815 E_{o_1}=\frac{1}{2}\left(\hat{y_{o_1}}-y_{o_1}\right)^2=\frac{1}{2}(0.01-0.91879937)^2=0.41295815 Eo1=21(yo1^yo1)2=21(0.010.91879937)2=0.41295815
同理,计算出 E o 2 = 0.29210614 E_{o_2}=0.29210614 Eo2=0.29210614
总误差即为 E total  = E o 1 + E o 2 = 0.41295815 + 0.29210614 = 0.70506429 E_{\text {total }}=E_{o_1}+E_{o_2}=0.41295815+0.29210614=0.70506429 Etotal =Eo1+Eo2=0.41295815+0.29210614=0.70506429

然后进行反向过程,反向传播算法的目的是更高效的计算梯度,从而更新参数值,使得总误差更小,也就是使实际输出更贴近我们期望输出。它是作为一个整体去更新整个神经网络的,反向就是先考虑输出层,然后再考虑上一层,直到输入层。
首先计算输出层:
考虑参数 ω 5 \omega_5 ω5,计算 ω 5 \omega_5 ω5的改变会对总误差有多大的影响,即计算 ∂ E t o t a l ∂ ω 5 \frac{\partial E_{total}}{\partial \omega_5} ω5Etotal,由链式法则有 ∂ E t o t a l ∂ ω 5 = ∂ E t o t a l ∂ a o 1 ∂ a o 1 ∂ z o 1 ∂ z o 1 ∂ ω 5 \frac{\partial E_{total}}{\partial \omega_5}=\frac{\partial E_{total}}{\partial a_{o_1}}\frac{\partial a_{o_1}}{\partial z_{o_1}}\frac{\partial z_{o_1}}{\partial \omega_5} ω5Etotal=ao1Etotalzo1ao1ω5zo1
要计算这个等式中的每个式子,首先计算 a o 1 a_{o_1} ao1如何影响总误差
E total  = 1 2 ∑ (  target  o 1 − a o 1 ) 2 + 1 2 (  target  o 2 − a o 2 ) 2 ∂ E total  ∂ a o 1 = 2 ∗ 1 2 (  target  o 1 − a o 1 ) ∗ ( − 1 ) + 0 = − (  target  o 1 − a o 1 ) = − ( 0.01 − 0.91879937 ) = 0.90879937 \begin{aligned} & E_{\text {total }}=\frac{1}{2} \sum\left(\text { target }_{o_1-a_{o_1}}\right)^2+\frac{1}{2}\left(\text { target }_{o_2}-a_{o_2}\right)^2 \\ & \frac{\partial E_{\text {total }}}{\partial a_{o_1}}=2 * \frac{1}{2}\left(\text { target }_{o_1-a_{o_1}}\right) *(-1)+0=-\left(\text { target }_{o_1}-a_{o_1}\right)=-(0.01-0.91879937)=0.90879937 \end{aligned} Etotal =21( target o1ao1)2+21( target o2ao2)2ao1Etotal =221( target o1ao1)(1)+0=( target o1ao1)=(0.010.91879937)=0.90879937

接下来计算 ∂ a o 1 ∂ z o 1 \frac{\partial a_{o_1}}{\partial z_{o_1}} zo1ao1
我们知道 σ ′ ( z ) = σ ( z ) ( 1 − σ ( z ) ) \sigma'(z)=\sigma(z)(1-\sigma(z)) σ(z)=σ(z)(1σ(z))(对sigmoid函数求导证明)
所以  所以  ∂ a o 1 ∂ z o 1 = a o 1 ( 1 − a o 1 ) = 0.91879937 ( 1 − 0.91879937 ) = 0.07460709 \text { 所以 } \frac{\partial a_{o_1}}{\partial z_{o_1}}=a_{o_1}\left(1-a_{o_1}\right)=0.91879937(1-0.91879937)=0.07460709  所以 zo1ao1=ao1(1ao1)=0.91879937(10.91879937)=0.07460709
最后是 ∂ z o 1 ∂ ω 5 \frac{\partial z_{o_1}}{\partial \omega_5} ω5zo1
z o 1 = ω 5 ∗ a h 1 + ω 6 ∗ a h 2 + b z_o1=\omega_5 * a_{h_1}+\omega_6 * a_{h_2}+b zo1=ω5ah1+ω6ah2+b
∂ z o 1 ∂ ω 5 = a h 1 = 0.98201379 \frac{\partial z_{o_1}}{\partial \omega_5}=a_{h_1}=0.98201379 ω5zo1=ah1=0.98201379
最后放到一起得到:
∂ E t o t a l ∂ ω 5 = ∂ E t o t a l ∂ a o 1 ∂ a o 1 ∂ z o 1 ∂ z o 1 ∂ ω 5 = 0.90879937 ∗ 0.07460709 ∗ 0.98201379 = 0.06658336 \frac{\partial E_{total}}{\partial \omega_5}=\frac{\partial E_{total}}{\partial a_{o_1}}\frac{\partial a_{o_1}}{\partial z_{o_1}}\frac{\partial z_{o_1}}{\partial \omega_5}=0.90879937*0.07460709*0.98201379=0.06658336 ω5Etotal=ao1Etotalzo1ao1ω5zo1=0.908799370.074607090.98201379=0.06658336
通常一般定义 δ o 1 = ∂ E t o t a l ∂ a o 1 ∂ a o 1 ∂ z o 1 = ∂ E t o t a l ∂ z o 1 \delta_{o_1}=\frac{\partial E_{total}}{\partial a_{o_1}}\frac{\partial a_{o_1}}{\partial z_{o_1}}=\frac{\partial E_{total}}{\partial z_{o_1}} δo1=ao1Etotalzo1ao1=zo1Etotal
因此, ∂ E t o t a l ∂ ω 5 = δ o 1 a h 1 \frac{\partial E_{total}}{\partial \omega_5}=\delta_{o_1}a_{h_1} ω5Etotal=δo1ah1
为了减小误差,通常需要更新当前权重,如下:
ω 5 = ω 5 − α ∗ ∂ E t o t a l ∂ ω 5 = 2 − 0.5 ∗ 0.06658336 = 1.96670832 \omega_5 = \omega_5 - \alpha * \frac{\partial E_{total}}{\partial \omega_5}=2-0.5*0.06658336=1.96670832 ω5=ω5αω5Etotal=20.50.06658336=1.96670832
同理可以算出其他权重 ω 6 = − 2.00911750 \omega_6=-2.00911750 ω6=2.00911750 ω 7 = − 1.93442139 \omega_7=-1.93442139 ω7=1.93442139 ω 8 = − 0.98204017 \omega_8=-0.98204017 ω8=0.98204017
这是输出层的所有参数,接下来需要往前推,更新隐藏层的参数。
首先来更新 ω 1 \omega_1 ω1:
∂ E t o t a l ∂ ω 1 = ∂ E t o t a l ∂ a h 1 ∂ a h 1 ∂ z h 1 ∂ z h 1 ∂ ω 1 \frac{\partial E_{total}}{\partial \omega_1}=\frac{\partial E_{total}}{\partial a_{h_1}}\frac{\partial a_{h_1}}{\partial z_{h_1}}\frac{\partial z_{h_1}}{\partial \omega_1} ω1Etotal=ah1Etotalzh1ah1ω1zh1
要用和更新输出层参数类似的步骤来更新隐藏层的参数,但是不同的是,每个隐藏层的神经元都影响了多个输出层(或下一层)神经元的输出, a h 1 a_{h_1} ah1同时影响了 a o 1 a_{o_1} ao1 a o 2 a_{o_2} ao2,因此计算 ∂ E t o t a l ∂ a h 1 \frac{\partial E_{total}}{\partial a_{h_1}} ah1Etotal需要将输出层的两个神经元都考虑在内:
∂ E t o t a l ∂ a h 1 = ∂ E o 1 ∂ a h 1 + ∂ E o 2 ∂ a h 1 \frac{\partial E_{total}}{\partial a_{h_1}}=\frac{\partial E_{o_1}}{\partial a_{h_1}}+\frac{\partial E_{o_2}}{\partial a_{h_1}} ah1Etotal=ah1Eo1+ah1Eo2,从 ∂ E o 1 ∂ a h 1 \frac{\partial E_{o_1}}{\partial a_{h_1}} ah1Eo1开始,有:
∂ E o 1 ∂ a h 1 = ∂ E o 1 ∂ z o 1 ∗ ∂ z o 1 ∂ a h 1 \frac{\partial E_{o_1}}{\partial a_{h_1}}=\frac{\partial E_{o_1}}{\partial z_{o_1}}* \frac{\partial z_{o_1}}{\partial a_{h_1}} ah1Eo1=zo1Eo1ah1zo1
上面已经算过 ∂ E o 1 ∂ z o 1 = 0.06780288 \frac{\partial E_{o_1}}{\partial z_{o_1}}=0.06780288 zo1Eo1=0.06780288了(实际上就是 ∂ E t o t a l ∂ z o 1 \frac{\partial E_{total}}{\partial z_{o_1}} zo1Etotal,因为 E t o t a l E_{total} Etotal只有 E o 1 E_{o_1} Eo1这一项对 z o 1 z_{o_1} zo1求导不为0),而且 ∂ z o 1 ∂ a h 1 = ω 5 = 2 \frac{\partial z_{o_1}}{\partial a_{h_1}}=\omega_5=2 ah1zo1=ω5=2,所以有 ∂ E o 1 ∂ a h 1 = 0.06780288 ∗ 2 = 0.13560576 \frac{\partial E_{o_1}}{\partial a_{h_1}}=0.06780288*2=0.13560576 ah1Eo1=0.067802882=0.13560576
同理,可得 ∂ E o 2 ∂ a h 1 = − 0.13355945 ∗ ( − 2 ) = 0.26711890 \frac{\partial E_{o_2}}{\partial a_{h_1}}=-0.13355945*(-2)=0.26711890 ah1Eo2=0.13355945(2)=0.26711890
因此:
∂ E t o t a l ∂ a h 1 = ∂ E o 1 ∂ a h 1 + ∂ E o 2 ∂ a h 1 = 0.13560576 + 0.26711890 = 0.40272466 \frac{\partial E_{total}}{\partial a_{h_1}}=\frac{\partial E_{o_1}}{\partial a_{h_1}}+\frac{\partial E_{o_2}}{\partial a_{h_1}}=0.13560576+0.26711890=0.40272466 ah1Etotal=ah1Eo1+ah1Eo2=0.13560576+0.26711890=0.40272466
现在已经知道了 ∂ E t o t a l ∂ a h 1 \frac{\partial E_{total}}{\partial a_{h_1}} ah1Etotal,还需要计算 ∂ a h 1 ∂ z h 1 \frac{\partial a_{h_1}}{\partial z_{h_1}} zh1ah1 ∂ z h 1 ∂ ω 1 \frac{\partial z_{h_1}}{\partial \omega_1} ω1zh1
a h 1 = σ ( z h 1 ) = 1 1 + e − z h 1 = 1 1 + e − 4 = 0.98201379 a_{h_1}=\sigma(z_{h_1})=\frac{1}{1+e^{-z_{h_1}}}=\frac{1}{1+e^{-4}}=0.98201379 ah1=σ(zh1)=1+ezh11=1+e41=0.98201379
∂ a h 1 ∂ z h 1 = a h 1 ( 1 − a h 1 ) = 0.98201379 ( 1 − 0.98201379 ) = 0.01766271 \frac{\partial a_{h_1}}{\partial z_{h_1}}=a_{h_1}(1-a_{h_1})=0.98201379(1-0.98201379)=0.01766271 zh1ah1=ah1(1ah1)=0.98201379(10.98201379)=0.01766271
z h 1 = ω 1 x 1 + ω 2 x 2 + b z_{h_1}=\omega_1 x_1+\omega_2 x_2 + b zh1=ω1x1+ω2x2+b
∂ z h 1 ∂ ω 1 = x 1 = 1 \frac{\partial z_{h_1}}{\partial \omega_1}=x_1=1 ω1zh1=x1=1
最后,总式子就可以计算了:
∂ E t o t a l ∂ ω 1 = ∂ E t o t a l ∂ a h 1 ∂ a h 1 ∂ z h 1 ∂ z h 1 ∂ ω 1 = 0.40272466 ∗ 0.01766271 ∗ 1 = 0.00711321 \frac{\partial E_{total}}{\partial \omega_1}=\frac{\partial E_{total}}{\partial a_{h_1}}\frac{\partial a_{h_1}}{\partial z_{h_1}}\frac{\partial z_{h_1}}{\partial \omega_1}=0.40272466*0.01766271*1=0.00711321 ω1Etotal=ah1Etotalzh1ah1ω1zh1=0.402724660.017662711=0.00711321
接下来就可以更新 ω 1 \omega_1 ω1
ω 1 = ω 1 − α ∗ E t o t a l ∂ ω 1 = 0.99644340 \omega_1=\omega_1 - \alpha * \frac{E_{total}}{\partial \omega_1}=0.99644340 ω1=ω1αω1Etotal=0.99644340
同理可以得到: ω 2 = − 1.99644340 \omega_2=-1.99644340 ω2=1.99644340 ω 3 = − 0.99979884 \omega_3=-0.99979884 ω3=0.99979884 ω 4 = 0.99979884 \omega_4=0.99979884 ω4=0.99979884
在执行10000此更新权重的过程后,误差变成了0.000,输出是0.011851540581436764和0.9878060737917571,这和期望输出0.01和0.99十分接近了。

使用numpy来练习上述过程:

import numpy as np

class Network():
    def __init__(self, **kwargs):
        self.w1, self.w2, self.w3, self.w4 = kwargs['w1'], kwargs['w2'], kwargs['w3'], kwargs['w4']
        self.w5, self.w6, self.w7, self.w8 = kwargs['w5'], kwargs['w6'], kwargs['w7'], kwargs['w8']
        self.d_w1, self.d_w2, self.d_w3, self.d_w4 = 0.0, 0.0, 0.0, 0.0
        self.d_w5, self.d_w6, self.d_w7, self.d_w8 = 0.0, 0.0, 0.0, 0.0
        self.x1 = kwargs['x1']
        self.x2 = kwargs['x2']
        self.y1 = kwargs['y1']
        self.y2 = kwargs['y2']
        self.learning_rate = kwargs['learning_rate']

    def sigmoid(self, z):
        a = 1 / (1 + np.exp(-z))
        return a

    def forward_propagate(self):
        loss = 0.0
        b = 1
        in_h1 = self.w1 * self.x1 + self.w2 * self.x2 + b
        out_h1 = self.sigmoid(in_h1)
        in_h2 = self.w3 * self.x1 + self.w4 * self.x2 + b
        out_h2 = self.sigmoid(in_h2)

        in_o1 = self.w5 * out_h1 + self.w6 * out_h2
        out_o1 = self.sigmoid(in_o1)
        in_o2 = self.w7 * out_h1 + self.w8 * out_h2
        out_o2 = self.sigmoid(in_o2)

        loss += (self.y1 - out_o1) ** 2 + (self.y2 - out_o2) ** 2
        loss = loss / 2

        return out_o1, out_o2, out_h1, out_h2, loss

    def back_propagate(self, out_o1, out_o2, out_h1, out_h2):
        d_o1 = (out_o1 - self.y1)
        d_o2 = (out_o2 - self.y2)

        d_w5 = d_o1 * out_o1 * (1 - out_o1) * out_h1
        d_w6 = d_o1 * out_o1 * (1 - out_o1) * out_h2

        d_w7 = d_o2 * out_o2 * (1 - out_o2) * out_h1
        d_w8 = d_o2 * out_o2 * (1 - out_o2) * out_h2

        d_w1 = (d_w5 + d_w6) * out_h1 * (1 - out_h1) * self.x1
        d_w2 = (d_w5 + d_w6) * out_h1 * (1 - out_h1) * self.x2

        d_w3 = (d_w7 + d_w8) * out_h2 * (1 - out_h2) * self.x1
        d_w4 = (d_w7 + d_w8) * out_h2 * (1 - out_h2) * self.x2

        self.d_w1, self.d_w2, self.d_w3, self.d_w4 = d_w1, d_w2, d_w3, d_w4
        self.d_w5, self.d_w6, self.d_w7, self.d_w8 = d_w5, d_w6, d_w7, d_w8
        return

    def update_w(self):
        self.w1 = self.w1 - self.learning_rate * self.d_w1
        self.w2 = self.w2 - self.learning_rate * self.d_w2
        self.w3 = self.w3 - self.learning_rate * self.d_w3
        self.w4 = self.w4 - self.learning_rate * self.d_w4
        self.w5 = self.w5 - self.learning_rate * self.d_w5
        self.w6 = self.w6 - self.learning_rate * self.d_w6
        self.w7 = self.w7 - self.learning_rate * self.d_w7
        self.w8 = self.w8 - self.learning_rate * self.d_w8

if __name__ == "__main__":
    w_key = ['w1', 'w2', 'w3', 'w4', 'w5', 'w6', 'w7', 'w8']
    w_value = [1, -2, -1, 1, 2, -2, -2, -1]
    parameter = dict(zip(w_key, w_value))
    parameter['x1'] = 1
    parameter['x2'] = -1
    parameter['y1'] = 0.01
    parameter['y2'] = 0.99
    parameter['learning_rate'] = 0.5
    network = Network(**parameter)

    for i in range(10000):
        out_o1, out_o2, out_h1, out_h2, loss = network.forward_propagate()
        if (i % 1000 == 0):
            print("第{}轮的loss={}".format(i,loss))
        network.back_propagate(out_o1, out_o2, out_h1, out_h2)
        network.update_w()

    print("更新后的权重")
    print(network.w1, network.w2, network.w3, network.w4, network.w5, network.w6, network.w7, network.w8)

输出为:

0轮的loss=0.71592427504641741000轮的loss=0.00033994112826449472000轮的loss=0.000121845330006650643000轮的loss=6.271954032855594e-054000轮的loss=3.751394416870217e-055000轮的loss=2.438595788224937e-056000轮的loss=1.6716935251649648e-057000轮的loss=1.1889923562720554e-058000轮的loss=8.688471135735563e-069000轮的loss=6.481437220727472e-06
更新后的权重
0.9057590485430621 -1.9057590485430547 0.4873077189729459 -0.4873077189729459 -1.130913420789734 -3.752510764474653 2.7328131233332877 1.948002277914531

使用pytorch来练习上述过程

import torch
from torch import nn


class Network(nn.Module):
    def __init__(self, w_value):
        super().__init__()
        self.sigmoid = nn.Sigmoid()
        self.linear1 = nn.Linear(2, 2, bias=True)
        self.linear1.weight.data = torch.tensor(w_value[:4], dtype=torch.float32).view(2, 2)
        self.linear1.bias.data = torch.ones(2)
        self.linear2 = nn.Linear(2, 2, bias=True)
        self.linear2.weight.data = torch.tensor(w_value[4:], dtype=torch.float32).view(2, 2)
        self.linear2.bias.data = torch.ones(2)

    def forward(self, x):
        x = self.linear1(x)
        x = self.sigmoid(x)
        x = self.linear2(x)
        x = self.sigmoid(x)
        return x

w_value = [1, -2, -1, 1, 2, -2, -2, -1]
network = Network(w_value)

loss_compute = nn.MSELoss()
learning_rate = 0.5
optimizer = torch.optim.SGD(network.parameters(), lr=learning_rate)

x1, x2 = 1, -1
y1, y2 = 0.01, 0.99
inputs = torch.tensor([x1, x2], dtype=torch.float32)
targets = torch.tensor([y1, y2], dtype=torch.float32)

for i in range(10000):
    optimizer.zero_grad()
    outputs = network(inputs)
    loss = loss_compute(outputs, targets)
    if (i % 1000 == 0):
        print("第{}轮的loss={}".format(i, loss))
    loss.backward()
    optimizer.step()

# 最终的权重和偏差
print("权重:")
print(network.linear1.weight)
print(network.linear2.weight)
print("偏差:")
print(network.linear1.bias)
print(network.linear2.bias)
0轮的loss=0.70506429672241211000轮的loss=0.00017875838966574522000轮的loss=5.7717210438568145e-053000轮的loss=2.7112449970445596e-054000轮的loss=1.487863755755825e-055000轮的loss=8.897048246581107e-066000轮的loss=5.617492206511088e-067000轮的loss=3.6816677493334282e-068000轮的loss=2.4793637294351356e-069000轮的loss=1.704187070572516e-06
权重:
Parameter containing:
tensor([[ 0.9223, -1.9223],
        [-0.0543,  0.0543]], requires_grad=True)
Parameter containing:
tensor([[-0.3244, -3.2635],
        [ 0.5369,  0.4165]], requires_grad=True)
偏差:
Parameter containing:
tensor([0.9223, 1.9457], requires_grad=True)
Parameter containing:
tensor([-1.3752,  3.5922], requires_grad=True)

函数拟合

问题描述

理论和实验证明,一个两层的ReLU网络可以模拟任何函数[1~5]。请自行定义一个函数, 并使用基于ReLU的神经网络来拟合此函数。

要求

  • 请自行在函数上采样生成训练集和测试集,使用训练集来训练神经网络,使用测试集来验证拟合效果。
  • 可以使用深度学习框架来编写模型。
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset
import torch.nn as nn
import numpy as np
import torch

# 准备数据
x1 = np.linspace(-2 * np.pi, 2 * np.pi, 400)
x2 = np.linspace(np.pi, -np.pi, 400)
y = np.sin(x1) + np.cos(3 * x2)
# 将数据做成数据集的模样
X = np.vstack((x1, x2)).T
Y = y.reshape(400, -1)
# 使用批训练方式
dataset = TensorDataset(torch.tensor(X, dtype=torch.float), torch.tensor(Y, dtype=torch.float))
dataloader = DataLoader(dataset, batch_size=100, shuffle=True)


# 神经网络主要结构,这里就是一个简单的线性结构

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.net = nn.Sequential(
            nn.Linear(in_features=2, out_features=10), nn.ReLU(),
            nn.Linear(10, 100), nn.ReLU(),
            nn.Linear(100, 10), nn.ReLU(),
            nn.Linear(10, 1)
        )

    def forward(self, input: torch.FloatTensor):
        return self.net(input)


net = Net()

# 定义优化器和损失函数
optim = torch.optim.Adam(Net.parameters(net), lr=0.001)
Loss = nn.MSELoss()

# 下面开始训练:
# 一共训练 1000次
for epoch in range(1000):
    loss = None
    for batch_x, batch_y in dataloader:
        y_predict = net(batch_x)
        loss = Loss(y_predict, batch_y)
        optim.zero_grad()
        loss.backward()
        optim.step()
    # 每100次 的时候打印一次日志
    if (epoch + 1) % 100 == 0:
        print("step: {0} , loss: {1}".format(epoch + 1, loss.item()))

# 使用训练好的模型进行预测
predict = net(torch.tensor(X, dtype=torch.float))

# 绘图展示预测的和真实数据之间的差异
import matplotlib.pyplot as plt

plt.plot(x1, y, label="fact")
plt.plot(x1, predict.detach().numpy(), label="predict")
plt.title("function")
plt.xlabel("x1")
plt.ylabel("sin(x1)+cos(3 * x2)")
plt.legend()
plt.show()

输出:

step: 100 , loss: 0.23763391375541687
step: 200 , loss: 0.06673044711351395
step: 300 , loss: 0.044088222086429596
step: 400 , loss: 0.013059427961707115
step: 500 , loss: 0.010913526639342308
step: 600 , loss: 0.003434327431023121
step: 700 , loss: 0.00702542532235384
step: 800 , loss: 0.001976138213649392
step: 900 , loss: 0.0032644111197441816
step: 1000 , loss: 0.003176396246999502

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/663814.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

R语言绘图 --- 折线图(Biorplot 开发日志 --- 1)

「写在前面」 在科研数据分析中我们会重复地绘制一些图形,如果代码管理不当经常就会忘记之前绘图的代码。于是我计划开发一个 R 包(Biorplot),用来管理自己 R 语言绘图的代码。本系列文章用于记录 Biorplot 包开发日志。 相关链接…

通过强化学习彻底改变大型数据集特征选择

文章目录 一、说明二、强化学习:特征选择的马尔可夫决策问题三、用于使用强化学习进行特征选择的 python 库3.1. 数据预处理3.2. 安装和导入FSRLearning库 四、结论和参考文献 一、说明 了解强化学习如何改变机器学习模型的特征选择。通过实际示例和专用的 Python 库…

Qt6.4.2基于CMake添加Qt3DCore模块报错

在文档中说明是添加 find_package(Qt6 REQUIRED COMPONENTS 3dcore) target_link_libraries(mytarget PRIVATE Qt6::3dcore)find_package是没有问题,但是target_link_libraries会报错,报拼写错误,无法链接上Qt6::3dcore 需要使用“3DCore”…

工厂如何最大化mes系统的价值

mes系统(Manufacturing Execution System)是现代工厂管理中的一个重要系统,它可以实现生产过程中的信息约束与控制,促进生产流程的跟踪和分析,提高生产效率及质量。 一、整合mes系统和erp系统 mes系统和erp系统是两个…

STM32 IIC协议

本文代码使用 HAL 库。 文章目录 前言一、什么是IIC协议二、IIC信号三、IIC协议的通讯时序1. 写操作2. 读操作 四、上拉电阻作用总结 前言 从这篇文章开始为大家介绍一些通信协议,包括 UART,SPI,IIC等。 UART串口通讯协议 SPI通信协议 一、…

【深度学习】YOLOv10实战:20行代码将笔记本摄像头改装成目标检测监控

目录 一、引言 二、YOLOv10视觉目标检测—原理概述 2.1 什么是YOLO 2.2 YOLO的网络结构 三、YOLOv10视觉目标检测—训练推理 3.1 YOLOv10安装 3.1.1 克隆项目 3.1.2 创建conda环境 3.1.3 下载并编译依赖 3.2 YOLOv10模型推理 3.2.1 模型下载 3.2.2 WebUI推理 …

微服务架构-微服务架构的挑战与微服务化的具体时机

目录 一、微服务架构的挑战 1.1 概述 1.2 服务拆分 1.3 开发挑战 1.4 测试挑战 1.4.1 开箱即用、一键部署的集成环境 1.4.2 测试场景和测试确定性 1.4.3 微服务相关的非功能测试 1.4.4 自动化测试 1.5 运维挑战 1.5.1 监控 1.5.2 部署 1.5.3 问题追查 1.5.4 依赖管…

chrome调试手机网页

前期准备 1、 PC端安装好chrmoe浏览器 2、 安卓手机安装好chrmoe浏览器 3、 数据线 原文地址:https://lengmo714.top/343880cb.html 手机打开调试模式 进入手机设置,找到开发者模式,然后启用USB调试 打开PC端chrome调试功能 1、点击chr…

视频汇聚平台EasyCVR对接GA/T 1400视图库:结构化数据(人员/人脸、车辆、物品)对象XMLSchema描述

在信息化浪潮席卷全球的背景下,公安信息化建设日益成为提升社会治理能力和维护社会稳定的关键手段。其中,GA/T 1400标准作为公安视频图像信息应用系统的核心规范,以其结构化数据处理与应用能力,为公安信息化建设注入了强大的动力。…

webpack5零基础入门-19HMR的应用

1.定义 HMR即HotModuleReplacement 开发时,当我们修改了其中一个模块的代码webpack默认会将所有模块重新打包编译,速度很慢所以我们需要做到修改摸个模块代码,只对这个模块的代码重新打包编译,其他模块不变,这样打包…

【excel】设置二级联动菜单

文章目录 【需求】在一级菜单选定后,二级菜单联动显示一级菜单下的可选项【步骤】step1 制作辅助列1.列转行2.在辅助列中匹配班级成员 之前做完了 【excel】设置可变下拉菜单(一级联动下拉菜单),开始做二级联动菜单。 【需求】在…

算法(六)计数排序

文章目录 计数排序技术排序简介算法实现 计数排序 技术排序简介 计数排序是利用数组下标来确定元素的正确位置的。 假定数组有10个整数,取值范围是0~10,可以根据这有限的范围,建立一个长度为11的数组。数组下标从0到10,元素初始…

【C++】哈希(2万字)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 unordered系列关联式容器 unordered_map unordered_map的文档介绍 unordered_map的接口说明 unordered_set 底层结构 哈希概念 哈希冲突 哈希函数 哈希…

【康耐视国产案例】Nvidia/算能+智能AI相机:用AI驱动 | 降低电动车成本的未来之路

受环保观念影响、政府激励措施推动与新能源技术的发展,消费者对电动汽车(EV)的需求正在不断增长,电动汽车已经成为了未来出行方式的重要组成部分。然而,电动汽车大规模取代燃油汽车的道路还很漫长。最大的障碍就是电动汽车的售价相对过高。尽…

新设立湖北投资管理公司流程和要求

在湖北投资管理企业进行注册时,需要准备一系列的材料并按照一定的流程进行办理。本文将从注册材料及注册流程两方面来介绍,帮助您了解注册投资管理企业的步骤和所需的具体材料。详情致电咨询我或者来公司面谈。 新注册材料要求: 企业名称申请书:要求提供…

vue路由跳转之【编程式导航与传参】

vue路由有两种跳转方式 ----> 编程式与声明式,本文重点讲解vue路由的【编程式导航 】【编程式导航传参 ( 查询参数传参 & 动态路由传参 ) 】等内容,并结合具体案例让小伙伴们深入理解 ,彻底掌握!创作不易,需要的…

汇编:调用C函数

在32位汇编程序中可以调用C函数;这种做法在很多情况下是有用的,尤其是在汇编程序需要与C代码进行交互或利用C语言的库函数时。下面是一些情况下使用汇编调用C函数的常见情景: ①优化性能:某些特定的任务可能用汇编语言编写更有效率…

[Linux]重定向

一、struct file内核对象 struct file是在内核中创建,专门用来管理被打开文件的结构体。struct file中包含了打开文件的所有属性,文件的操作方法集以及文件缓冲区(无论读写,我们都需要先将数据加载到文件缓冲区中。)等…

POP —— Nodes DOP

POP Advect by Volumes —— 使用速度场驱动粒子 此节点被设计为更容易通过流体来驱动粒子,通常流体被单独模拟并从磁盘上读取速度场;此操作将修改force、vel、P属性; Update Force,调整粒子的加速度,类似POP Force&a…

RAID技术迭代、原理对比、产品梳理(HCIA)

目录 一、RAID技术迭代 传统RAID LUN虚拟化2.0 工作原理: 块虚拟化2.0 为什么有RAID2.0? RAID2.0实现原理: RAID-TPRAID 7 华为RAID-TP技术 RAID的4种工作状态 RAID算法 普通RAID算法 华为动态RAID算法 保险箱盘(存掉…