Deep Learning With Pytorch - 最基本的感知机、贯序模型/分类、拟合

文章目录

如何利用pytorch创建一个简单的网络模型?

Step1. 感知机,多层感知机(MLP)的基本结构

感知机(Perceptron)是神经网络中的基本单元,神经网络的雏形,也被称作神经元(原理就是仿照生物上的神经元)、单层神经网络。

通过设置不同的权重,并加上一个激活函数(判决门限),就构成了一个单层感知机的基本网络结构,可以实现与或非三种基本逻辑:
在这里插入图片描述
在这里插入图片描述
但是单层感知机的功能还是具有局限性,因为它毕竟只是一种二元线性分类模型(其输入为实例的特征向量,输出为实例的类别,取1和0【sigmoid激活判决】或+1和-1【sign激活判决】 ),像同或、异或这种稍微复杂一点的逻辑,就无法用单层感知机拟合出结果:
在这里插入图片描述
所以通过扩展感知机的层数,引入更多层的神经元(多层感知机MLP的由来),从而带来更多可以训练的参数,得到一种非线性模型,以达到拟合出预期的效果:
在这里插入图片描述
加入一层隐层网络之后,同或数据集就变得可以拟合了:

a 1 [ 1 ] = s i g m o i d ( ω 1 , 1 [ 1 ] ⋅ x 1 + ω 2 , 1 [ 1 ] ⋅ x 2 + b 1 [ 1 ] ) a_1^{[1]}=sigmoid(\omega_{1,1}^{[1]}·x_1+\omega_{2,1}^{[1]}·x_2+b_1^{[1]}) a1[1]=sigmoid(ω1,1[1]x1+ω2,1[1]x2+b1[1])

a 2 [ 1 ] = s i g m o i d ( ω 2 , 1 [ 1 ] ⋅ x 1 + ω 2 , 2 [ 1 ] ⋅ x 2 + b 2 [ 1 ] ) a_2^{[1]}=sigmoid(\omega_{2,1}^{[1]}·x_1+\omega_{2,2}^{[1]}·x_2+b_2^{[1]}) a2[1]=sigmoid(ω2,1[1]x1+ω2,2[1]x2+b2[1])

a 1 [ 2 ] = s i g m o i d ( ω 1 , 1 [ 2 ] ⋅ a 1 [ 1 ] + ω 2 , 1 [ 2 ] ⋅ a 2 [ 1 ] + b [ 2 ] ) a_1^{[2]}=sigmoid(\omega_{1,1}^{[2]}·a_1^{[1]}+\omega_{2,1}^{[2]}·a_2^{[1]}+b^{[2]}) a1[2]=sigmoid(ω1,1[2]a1[1]+ω2,1[2]a2[1]+b[2])【逻辑值】

上标 [ i ] ^{[i]} [i]代表第几层;
在这里插入图片描述

Step2. 超平面 ω T ⋅ x + b = 0 \omega^{T}·x+b=0 ωTx+b=0 or ω T ⋅ x = b \omega^{T}·x=b ωTx=b

初学者第一次见到 ω T ⋅ x + b = 0 \omega^{T}·x+b=0 ωTx+b=0这个表达式时,会觉得它非常像线性函数, ω T ⋅ x + b = 0 \omega^{T}·x+b=0 ωTx+b=0为什么是一条斜线呢?实际上这只是为了在二维平面更好表示其线性分类效果;
在这里插入图片描述
在三维空间中,分类效果是这样:
在这里插入图片描述
投影在 XOZ/YOZ 轴平面就是二维平面中所看到的效果。

在高等数学中我们学习过三维平面的一般表达式: A x + B y + C z + D = 0 Ax+By+Cz+D=0 Ax+By+Cz+D=0

( A , B , C ) (A, B, C) (A,B,C)为平面的法向量,亦可写为点法式: A ( x − x 0 ) + B ( y − y 0 ) + C ( z − z 0 ) = 0 A(x-x_0)+B(y-y_0)+C(z-z_0)=0 A(xx0)+B(yy0)+C(zz0)=0

( x 0 , y 0 , z 0 ) (x_0, y_0, z_0) (x0,y0,z0)是平面上的一个点,将点法式拆开: A x + B y + C z = A x 0 + B y 0 + C z 0 Ax+By+Cz=Ax_0+By_0+Cz_0 Ax+By+Cz=Ax0+By0+Cz0

这里的 A x 0 + B y 0 + C z 0 Ax_0+By_0+Cz_0 Ax0+By0+Cz0就是一般式中的 D D D.

当扩展至N维超平面时,式子就变成了: A ( x 1 − x 0 ) + B ( x 2 − x 0 ) + C ( x 3 − x 0 ) + . . . + N ( x n − x 0 ) = 0 A(x_1-x_0)+B(x_2-x_0)+C(x_3-x_0)+...+N(x_n-x_0)=0 A(x1x0)+B(x2x0)+C(x3x0)+...+N(xnx0)=0

A x 1 + B x 2 + C x 3 + . . . N x n = A x 0 + B x 0 + C x 0 + . . . N x 0 Ax_1+Bx_2+Cx_3+...Nx_n=Ax_0+Bx_0+Cx_0+...Nx_0 Ax1+Bx2+Cx3+...Nxn=Ax0+Bx0+Cx0+...Nx0

改写成向量相乘的形式:

令行向量 ω T = [ ω 1 , ω 2 , . . . , ω n ] = [ A , B , . . . , N ] \omega^T = [\omega_1, \omega_2,...,\omega_n]=[A, B,..., N] ωT=[ω1,ω2,...,ωn]=[A,B,...,N]

列向量 x = [ x 1 , x 2 , . . . , x n ] ′ x = [x_1, x_2, ... ,x_n]' x=[x1,x2,...,xn] b = A x 0 + B x 0 + C x 0 + . . . N x 0 b = Ax_0+Bx_0+Cx_0+...Nx_0 b=Ax0+Bx0+Cx0+...Nx0

则N维超平面的定义式: ω T ⋅ x = b \omega^{T}·x=b ωTx=b 就产生了, b b b为超平面的常数项截距, ω T \omega^{T} ωT是超平面的法向量。

感知机函数

Step1中我们见过了感知机加激活函数得到与门的效果:
在这里插入图片描述
感知机函数可以表示为: S i g m o i d ( ω T x + b ) Sigmoid(\omega^{T}x+b) Sigmoid(ωTx+b)

S i g m o i d ( ω T ⋅ x + b ) = { 1 , ω T x + b ≥ 0 0 , ω T x + b < 0 Sigmoid(\omega^{T}·x+b) = \begin{cases} 1, \qquad \omega^{T}x+b≥0\\ 0,\qquad \omega^{T}x+b<0\end{cases} Sigmoid(ωTx+b)={1,ωTx+b00,ωTx+b<0

S i g n ( ω T ⋅ x + b ) = { + 1 , ω T x + b ≥ 0 − 1 , ω T x + b < 0 Sign(\omega^{T}·x+b) = \begin{cases} +1, \qquad \omega^{T}x+b≥0\\ -1,\qquad \omega^{T}x+b<0\end{cases} Sign(ωTx+b)={+1,ωTx+b01,ωTx+b<0

我们现在采用 S i g n Sign Sign 符号函数作为激活函数,利用超平面 ω T x + b = 0 \omega^{T}x+b=0 ωTx+b=0 进行决策分类任务,并定义输出标签 y = + 1 y=+1 y=+1 时为“是”, y = − 1 y=-1 y=1 时为“否”;

则分类出现错误时必定会有 y ⋅ ( ω T x + b ) < 0 y·(\omega^{T}x+b)<0 y(ωTx+b)<0.

因此,我们定义损失函数: L ( ω , b ) = − ∑ i = 1 n y i ⋅ ( ω i T x + b ) \mathcal{L}(\omega,b)=-\sum_{i=1}^{n}y_i·(\omega^{T}_ix+b) L(ω,b)=i=1nyi(ωiTx+b)

实际上损失函数的定义也并非这么直截了当就得出,因为还要保证损失函数连续可导,才能对其求偏导进行 a r g m i n ( ω , b ) argmin(\omega, b) argmin(ω,b);极小化损失函数的过程不是一次使得所有误分类点的梯度下降,而是一次随机选取一个误分类点使其梯度下降,而损失函数中 y i ⋅ ( ω i T x + b ) y_i·(\omega^{T}_ix+b) yi(ωiTx+b)的由来,其实就是选取了误分类点到超平面距离公式中的分子项,分母项是个L2范数只起到了缩放作用,不影响损失函数的优化,所以可以忽略不计。
具体推导过程可以参考:感知机w·x+b=0怎么理解?数学推导是什么样的?

我们的目标就是想让损失函数尽可能地小,并选取使得损失函数最小的 w w w b b b.

利用经典的随机梯度下降(SGD)算法对损失函数进行优化训练:

{ ∂ L ∂ ω = L ( ω , b ) = − ∑ i = 1 n y i ⋅ x ∂ L ∂ b = L ( ω , b ) = − ∑ i = 1 n y i \begin{cases}\frac{\partial{L}}{\partial{\omega}}=\mathcal{L}(\omega,b)=-\sum_{i=1}^{n}y_i·x\\\frac{\partial{L}}{\partial{b}}=\mathcal{L}(\omega,b)=-\sum_{i=1}^{n}y_i\end{cases} {ωL=L(ω,b)=i=1nyixbL=L(ω,b)=i=1nyi

{ ω = ω + α ∂ L ∂ ω b = b + α ∂ L ∂ b \begin{cases}\omega = \omega + \alpha\frac{\partial{L}}{\partial{\omega}}\\ b = b + \alpha\frac{\partial{L}}{\partial{b}}\end{cases} {ω=ω+αωLb=b+αbL

利用感知机进行线性分类的训练过程如上,这也是支持向量机(SVM)算法的雏形。

Step3. 利用感知机进行决策分类的训练过程 -【Matlab代码】

clc,clear,close all;
%% 定义变量
n = 50;        % 正负样本的个数,总样本数为2n
r = 0.5;       % 学习率
m = 2;         % 样本的维数
i_max = 100;  % 最大迭代次数

%% 生成样本(以二维为例)
pix = linspace(-pi,pi,n);
randx = 2*pix.*rand(1,n) - pi;
x1 = [cos(randx) + 2*rand(1,n); 3+sin(randx) + 2*rand(1,n)];
x2 = [3+cos(randx) + 2*rand(1,n); sin(randx) + 2*rand(1,n)];
x = [x1'; x2'];  % 一共2n个点
y = [ones(n,1); -ones(n,1)];  %添加标签
figure(1)
hold on; 
plot(x1(1,:),x1(2,:),'rx'); 
plot(x2(1,:),x2(2,:),'go'); 

%% 训练感知机
x = [ones(2*n,1) x];    % 增加一个常数偏置项 [1, x1;x2]
w = zeros(1,m+1);       % 初始化权值 [w0, w1, w2]
flag = true;            % 退出循环的标志,为true时退出循环
for i=1:i_max 
    for j=1:2*n 
        if sign(x(j,:)*w') ~= y(j)  % 超平面加激活函数:sign(w'x+w0)
            disp(num2str(sign(x(j,:)*w')))
            disp(y(j))
            flag = false; 
            w = w + r*y(j)*x(j,:);  % 利用SGD算法更新参数
            % begin
            pause(0.3);
            cla('reset');
            axis([-1,6,-1,6]);
            hold on
            plot(x1(1,:),x1(2,:),'rx'); 
            plot(x2(1,:),x2(2,:),'go');
            x_test = linspace(0,5,20); 
            y_test = -w(2)/w(3).*x_test-w(1)/w(3); 
            plot(x_test,y_test,'m-.');
            % end
            M=getframe(gcf);
            nn=frame2im(M);
            [nn,cm]=rgb2ind(nn,256);
            if i==1
                imwrite(nn,cm,'out.gif','gif','LoopCount',inf,'DelayTime',0.1);
            else
                imwrite(nn,cm,'out.gif','gif','WriteMode','append','DelayTime',0.5)
            end
        end 
    end 
    if flag
        disp(num2str(sign(x(j,:)*w')))
        disp(y(j))
        break; 
    end 
end
disp(num2str(sign(x(j,:)*w')))
disp(y(j))

%% 画分割线
cla('reset');
hold on
axis([-1,6,-1,6]);
plot(x1(1,:),x1(2,:),'rx'); 
plot(x2(1,:),x2(2,:),'go');
x_test = linspace(0,5,20); 
y_test = -w(2)/w(3).*x_test-w(1)/w(3);  
plot(x_test,y_test,'linewidth',2);
legend('标签为正的样本','标签为负的样本','分类超平面');
M=getframe(gcf);
nn=frame2im(M);
[nn,cm]=rgb2ind(nn,256);
imwrite(nn,cm,'out.gif','gif','WriteMode','append','DelayTime',0.5)

程序中的超平面就是一条二维直线: ω 1 + ω 2 x 1 + ω 3 x 2 = 0 \omega_1+\omega_2x_1+\omega_3x_2=0 ω1+ω2x1+ω3x2=0

训练出的超平面纵坐标 y t e s t = x 2 = − ω 1 / ω 3 − ω 2 x 1 / ω 3 y_{test}=x_2=-\omega_1/\omega_3-\omega_2x_1/\omega_3 ytest=x2=ω1/ω3ω2x1/ω3.

在这里插入图片描述

打印观察 S i g n ( ω T ⋅ x + b ) Sign(\omega^{T}·x+b) Sign(ωTx+b)和标签 y y y的值:

disp(num2str(sign(x(j,:)*w')))
            disp(y(j))
0
     1

1
    -1

-1
     1

1
    -1

-1
     1

1
    -1

-1
     1

1
    -1

-1
     1

1
    -1

-1
     1

1
    -1

-1
     1

1
    -1

-1
     1

1
    -1

-1
    -1

可以看到当 S i g n ( ω T ⋅ x + b ) = = y Sign(\omega^{T}·x+b)==y Sign(ωTx+b)==y的时候终止迭代循环,得到正确分类结果。

从线性回归到贯序模型

除了感知机,ML入门时我们还会使用线性回归和逻辑回归这类经典统计分析方法。线性回归就是想要通过数据,训练出一个符合数据变化规律的超平面(二维中的超平面就是一条直线,三维中的超平面是一个平面)进行未来数据的预测。
正是因为实际问题中的数据可能是复杂多变的,所以仅靠线性的超平面不一定能得到最好的拟合效果,所以可以通过添加其他的网络结构,添加非线性的隐藏层来实现具有更复杂拟合能力的网络结构。
在这里插入图片描述

而所谓的贯序模型就是将自定义的不同层网络(可以是线性层,激活函数层,卷积层、池化层、循环层、注意力层等等)串成一个模型。

# 添加激活层后的贯序模型
seq_model = nn.Sequential(OrderedDict([
    ('input_linear', nn.Linear(1, 12)),
    ('hidden_activation', nn.Tanh()),
    ('output_linear', nn.Linear(12, 1))
]))

该模型的网络结构包括:
输入层:第一层是一个线性层 (nn.Linear(1, 12)),它将接受一个维度为1的输入。
隐藏层:包含一个激活函数层 (nn.Tanh())。该隐藏层不改变维度,仅应用激活函数。
输出层:包含一个线性层 (nn.Linear(12, 1)),将隐藏层的输出维度(12)投影到一个维度为1的输出。
因此,该模型总共有三层:输入层、隐藏层和输出层。输入层的维度为1,输出层的维度为1。

nn.Linear(_, _)

nn.Linear 是 PyTorch 中的一个类,也可以理解为一个函数,用于定义一个线性变换(也称为全连接层或仿射变换),将输入特征映射到输出特征。它是神经网络模块 nn 提供的一个常用函数之一。

nn.Linear(in_features, out_features)中的第一个参数为in_features: 输入特征的数量(维度)。这个参数决定了输入的大小,通常也就是数据集中的特征数,即输入张量的最后一维大小;

nn.Linear(in_features, out_features)中的第二个参数为out_features: 输出特征的数量(维度)。这个参数决定了输出的大小,即输出张量的最后一维大小。

bias: 是否在变换中使用偏置项(偏置向量)。默认为 True,表示会使用偏置项;设置为 False 则不使用偏置项。
前向传播计算: 在神经网络的前向传播过程中,nn.Linear 定义的线性变换会对输入特征进行矩阵乘积运算,然后加上偏置项。具体计算公式如下:

output = input × weight ⊤ + bias \text{output} = \text{input} \times \text{weight}^{\top} + \text{bias} output=input×weight+bias

其中,in_features是需要严格按照数据集中需要训练的特征数确定的,如波士顿房价数据集中:

0-505就是数据集的batch_size,batch_size表示一次选多少行数据进行训练,而crim, age, tax…这类的特征一共14个特征数量,就是nn.Linear中的in_features了,当然大小也是根据你需要哪几个特征参与训练确定的。

在这里插入图片描述
nn,Linear线性层计算的输入和输出格式:

在这里插入图片描述

相比in_features, out_features 就灵活多变一些。如果 out_features 设置得太大,模型可能会过于复杂,导致过拟合问题。相反,如果设置得太小,模型可能无法捕捉足够的特征,导致欠拟合问题。选择适当的输出特征数量是在训练集和验证集上达到良好性能的关键之一。

nn.Linear 在神经网络中非常常见,它可以用于构建模型的一层或多层,实现从输入到输出的特征变换。通过多层的堆叠和非线性激活函数的引入,可以构建出更复杂的神经网络模型,适用于各种任务。

模型训练

在pytorch中我们通过定义一个training_loop来指定模型训练时的迭代次数,所选优化方法,调用创建的网络模型,以及选取的损失函数、训练集/验证集:

def training_loop(n_epochs, optimizer, model, loss_fn, t_u_train, t_u_val,
                  t_c_train, t_c_val):
    for epoch in range(1, n_epochs + 1):
        t_p_train = model(t_u_train)
        loss_train = loss_fn(t_p_train, t_c_train)
        t_p_val = model(t_u_val)
        loss_val = loss_fn(t_p_val, t_c_val)
        optimizer.zero_grad()   # 清除旧梯度
        loss_train.backward()   # 后向传播计算新梯度
        optimizer.step()        # 根据梯度进行SGD优化

        if epoch == 1 or epoch % 500 == 0:
            print(f"Epoch {epoch}, Training loss {loss_train.item():.4f},"
                  f" Validation loss {loss_val.item():.4f}")

def loss_fn(t_p, t_c):
    squared_diffs = (t_p - t_c) ** 2
    return squared_diffs.mean()

linear_model = nn.Linear(1, 1)
optimizer = optim.SGD(linear_model.parameters(), lr=1e-2)

# 尝试线性模型训练
training_loop(
    n_epochs=3000,
    optimizer=optimizer,
    model=linear_model,
    # loss_fn=loss_fn,
    loss_fn=nn.MSELoss(),
    t_u_train=t_un_train,
    t_u_val=t_un_val,
    t_c_train=t_c_train,
    t_c_val=t_c_val)

贯序模型例程 -【Pytorch完整代码】

import torch
import torch.optim as optim
import torch.nn as nn
from collections import OrderedDict
from matplotlib import pyplot as plt

torch.set_printoptions(edgeitems=2, linewidth=75)

# 数据集准备
t_c = [0.5, 14.0, 15.0, 28.0, 11.0, 8.0, 3.0, -4.0, 6.0, 13.0, 21.0]
t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
t_c = torch.tensor(t_c).unsqueeze(1)
t_u = torch.tensor(t_u).unsqueeze(1)

n_samples = t_u.shape[0]  # 获取数据集的样本数量(数据集中元素的数量)
n_val = int(0.2 * n_samples)  # 计算验证集的样本数量。这里使用了0.2作为验证集的比例,将数据集中的20%作为验证集

# 生成一个长度为n_samples的随机排列的索引数组。这里使用torch.rand-perm函数生成一个随机排列的整数数组,用于打乱原始数据集的索引顺序
shuffled_indices = torch.randperm(n_samples)
train_indices = shuffled_indices[:-n_val]
val_indices = shuffled_indices[-n_val:]
t_u_train = t_u[train_indices]
t_c_train = t_c[train_indices]
t_u_val = t_u[val_indices]
t_c_val = t_c[val_indices]
t_un_train = 0.1 * t_u_train
t_un_val = 0.1 * t_u_val

linear_model = nn.Linear(1, 1)
linear_model(t_un_val)

def training_loop(n_epochs, optimizer, model, loss_fn, t_u_train, t_u_val,
                  t_c_train, t_c_val):
    for epoch in range(1, n_epochs + 1):
        t_p_train = model(t_u_train)
        loss_train = loss_fn(t_p_train, t_c_train)
        t_p_val = model(t_u_val)
        loss_val = loss_fn(t_p_val, t_c_val)
        optimizer.zero_grad()   # 清除旧梯度
        loss_train.backward()   # 后向传播计算新梯度
        optimizer.step()        # 根据梯度进行SGD优化

        if epoch == 1 or epoch % 500 == 0:
            print(f"Epoch {epoch}, Training loss {loss_train.item():.4f},"
                  f" Validation loss {loss_val.item():.4f}")

def loss_fn(t_p, t_c):
    squared_diffs = (t_p - t_c) ** 2
    return squared_diffs.mean()

linear_model = nn.Linear(1, 1)
optimizer = optim.SGD(linear_model.parameters(), lr=1e-2)

# 尝试线性模型训练
training_loop(
    n_epochs=3000,
    optimizer=optimizer,
    model=linear_model,
    # loss_fn=loss_fn,
    loss_fn=nn.MSELoss(),
    t_u_train=t_un_train,
    t_u_val=t_un_val,
    t_c_train=t_c_train,
    t_c_val=t_c_val)

print()
print(linear_model.weight)
print(linear_model.bias)

# 添加激活层后的贯序模型
seq_model = nn.Sequential(OrderedDict([
    ('input_linear', nn.Linear(1, 12)),
    ('hidden_activation', nn.Tanh()),
    ('output_linear', nn.Linear(12, 1))
]))

print(seq_model)
print([param.shape for param in seq_model.parameters()])

for name, param in seq_model.named_parameters():
    print(name, param.shape)

optimizer = optim.SGD(seq_model.parameters(), lr=1e-3)

training_loop(
    n_epochs=5000,
    optimizer=optimizer,
    model=seq_model,        # 使用贯序模型重新训练
    loss_fn=nn.MSELoss(),
    t_u_train=t_un_train,
    t_u_val=t_un_val,
    t_c_train=t_c_train,
    t_c_val=t_c_val)

# 打印模型参数训练结果:
# print('output', seq_model(t_un_val))
# print('answer', t_c_val)
# print('hidden', seq_model.hidden_linear.weight.grad)

t_range = torch.arange(20., 90.).unsqueeze(1)

fig = plt.figure(dpi=100)
plt.xlabel("Fahrenheit")
plt.ylabel("Celsius")
plt.plot(t_u.numpy(), t_c.numpy(), 'o')
plt.plot(t_range.numpy(), seq_model(0.1 * t_range).detach().numpy(), 'c-')
plt.plot(t_u.numpy(), seq_model(0.1 * t_u).detach().numpy(), 'kx')
plt.show()

打印结果:

Epoch 1, Training loss 287.7947, Validation loss 243.3686
Epoch 500, Training loss 6.3782, Validation loss 5.3946
Epoch 1000, Training loss 2.9283, Validation loss 6.1271
Epoch 1500, Training loss 2.4918, Validation loss 6.4090
Epoch 2000, Training loss 2.4366, Validation loss 6.5120
Epoch 2500, Training loss 2.4296, Validation loss 6.5489
Epoch 3000, Training loss 2.4288, Validation loss 6.5621

Parameter containing:
tensor([[5.4817]], requires_grad=True)
Parameter containing:
tensor([-17.4273], requires_grad=True)
Sequential(
  (hidden_linear): Linear(in_features=1, out_features=12, bias=True)
  (hidden_activation): Tanh()
  (output_linear): Linear(in_features=12, out_features=1, bias=True)
)
[torch.Size([12, 1]), torch.Size([12]), torch.Size([1, 12]), torch.Size([1])]
hidden_linear.weight torch.Size([12, 1])
hidden_linear.bias torch.Size([12])
output_linear.weight torch.Size([1, 12])
output_linear.bias torch.Size([1])
Epoch 1, Training loss 200.8066, Validation loss 149.6482
Epoch 500, Training loss 8.0419, Validation loss 6.9692
Epoch 1000, Training loss 2.8967, Validation loss 9.0610
Epoch 1500, Training loss 1.7860, Validation loss 8.2857
Epoch 2000, Training loss 1.4266, Validation loss 7.6947
Epoch 2500, Training loss 1.3101, Validation loss 7.3714
Epoch 3000, Training loss 1.2710, Validation loss 7.2340
Epoch 3500, Training loss 1.2550, Validation loss 7.2035
Epoch 4000, Training loss 1.2451, Validation loss 7.2175
Epoch 4500, Training loss 1.2367, Validation loss 7.2404
Epoch 5000, Training loss 1.2289, Validation loss 7.2637

这个例子中虽然采用的输入层和输出层都是线性层nn.Linear,但是最终拟合出的却是曲线,而非二维的超平面(一条直线),这就是因为隐藏层我们选用了tanh函数,是非线性的,也因此提升了网络的拟合效果:
在这里插入图片描述
如果我们将nn.Tanh()改为nn.Relu()激活函数,拟合出来的就是一条直线了:
在这里插入图片描述

参考文献:
[1] Pytorch官方 - Deep Learning With Pytorch
[2] 《零基础学机器学习》
[3] 感知机 - 谢晋- 算法与数学之美
[4] 感知机w·x+b=0怎么理解?数学推导是什么样的?
[5] 啥都断更的小c-从0开始深度学习:什么是线性层?pytorch中nn.linear怎么用?
[6] 机器学习| 算法笔记-线性回归(Linear Regression)

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

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

相关文章

虚拟机问题

虚拟机无法识别USB设备 经排查为VMware USB Arbitration Service 没有启动,但是VMware USB Arbitration Service依赖于VMware Workstation Server启动 VMware USB Arbitration Service(VMUSBArbService)是由 VMware 虚拟化软件提供的一个服务,用于协调和管理主机系统上的…

Flink CDC系列之:基于 Flink CDC 构建 MySQL 和 Postgres 的 Streaming ETL

Flink CDC系列之&#xff1a;基于 Flink CDC 构建 MySQL 和 Postgres 的 Streaming ETL 一、技术路线二、MySQL数据库建表三、PostgreSQL数据库建表四、在 Flink SQL CLI 中使用 Flink DDL 创建表五、关联订单数据并且将其写入 Elasticsearch 中六、Kibana查看商品和物流信息的…

leetcode611. 有效三角形的个数(java)

有效三角形的个数 有效三角形的个数排序加二分排序 双指针 上期算法 有效三角形的个数 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 示例 1: 输入: nums [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使…

如何修复损坏的DOC和DOCX格式Word文件?

我们日常办公中&#xff0c;经常用到Word文档。但是有时会遇到word文件损坏、无法打开的情况。这时该怎么办&#xff1f;接着往下看&#xff0c;小编在这里就给大家带来最简单的Word文件修复方法&#xff01; 很多时候DOC和DOCX Word文件会无缘无故的损坏无法打开&#xff0c;一…

【C++ 记忆站】引用

文章目录 一、引用概念二、引用特性1、引用在定义时必须初始化2、一个变量可以有多个引用3、引用一旦引用一个实体&#xff0c;再不能引用其他实体 三、常引用四、使用场景1、做参数1、输出型参数2、大对象传参 2、做返回值1、传值返回2、传引用返回 五、传值、传引用效率比较六…

【C语言】每日一题(找到所有数组中消失的数字)

找到所有数组中消失的数字&#xff0c;链接奉上。 这里简单说一下&#xff0c;因为还没有接触到动态内存&#xff0c;数据结构&#xff0c;所以知识有限&#xff0c;也是尽力而为&#xff0c;结合题库的评论区找到了适合我的解法&#xff0c;以后有机会&#xff0c;会补上各种…

图数据库_Neo4j中文版_Centos7.9安装Neo4j社区版3.5.9_基于jdk1.8---Neo4j图数据库工作笔记0012

由于我们在国内使用啊,具体还是要用中文版滴,找了好久这个neo4j,原来还是有中文版的, https://we-yun.com/doc/neo4j-chs/ 中文版下载地址在这里: 所有版本都在这里了,需要哪个自己去下载就可以了,要注意下载以后,参考: https://we-yun.com/blog/prod-56.html 在这个位置下载…

画质提升+带宽优化,小红书音视频团队端云结合超分落地实践

随着视频业务和短视频播放规模不断增长&#xff0c;小红书一直致力于研究&#xff1a;如何在保证提升用户体验质量的同时降低视频带宽成本&#xff1f; 在近日结束的音视频技术大会「LiveVideoStackCon 2023」上海站中&#xff0c;小红书音视频架构视频图像处理算法负责人剑寒向…

2023.8 - java - 对象和类

public class Dog {String breed;int size;String colour;int age;void eat() {}void run() {}void sleep(){}void name(){} } 一个类可以包含以下类型变量&#xff1a; 局部变量&#xff1a;在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方…

实现Java异步调用的高效方法

文章目录 为什么需要异步调用&#xff1f;Java中的异步编程方式1. 使用多线程2. 使用Java异步框架 异步调用的关键细节结论 &#x1f389;欢迎来到Java学习路线专栏~实现Java异步调用的高效方法 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博…

LabVIEW开发最小化5G系统测试平台

LabVIEW开发最小化5G系统测试平台 由于具有大量存储能力和数据的应用程序的智能手机的激增&#xff0c;当前一代产品被迫提高其吞吐效率。正交频分复用由于其卓越的品质&#xff0c;如单抽头均衡和具有成本效益的实施&#xff0c;现在被广泛用作物理层技术。这些好处是以严格的…

Azure存储访问层

blob数据的热访问层&#xff0c;冷访问层和存档访问层 Azure Blob 存储是一种托管对象存储服务&#xff0c;可用于存储和访问大量非结构化数据&#xff0c;如文本和二进制数据。Azure Blob 存储提供了三个不同层级的访问方式&#xff0c;以适应不同数据的使用模式和成本效益需…

手把手教学——终端工具xshell与文件传输工具xftp使用步骤及详解

前言 xshell是一款常用于连接本地linux服务以及云服务器的终端远程连接工具&#xff0c;该款终端工具常搭配远程文件传输工具xftp一起使用&#xff0c;由于还有很多小伙伴还不知道这两款终端工具的使用流程及步骤&#xff0c;Darren洋在这里给小伙伴们进行详细讲解。 一、下载工…

proteus结合keil-arm编译器构建STM32单片机项目进行仿真

proteus是可以直接创建设计图和源码的&#xff0c;但是源码编译它需要借助keil-arm编译器&#xff0c;也就是我们安装keil-mdk之后自带的编译器。 下面给出一个完整的示例&#xff0c;主要是做一个LED灯闪烁的效果。 新建工程指定路径&#xff0c;Schematic,PCB layout都选择默…

【马蹄集】第二十三周——进位制专题

进位制专题 目录 MT2186 二进制&#xff1f;不同&#xff01;MT2187 excel的烦恼MT2188 单条件和MT2189 三进制计算机1MT2190 三进制计算机2 MT2186 二进制&#xff1f;不同&#xff01; 难度&#xff1a;黄金    时间限制&#xff1a;1秒    占用内存&#xff1a;128M 题目…

推荐一个绘图平台(可替代Visio)

不废话&#xff0c;简易记网址&#xff1a; draw.io 网站会重定向到&#xff1a;https://app.diagrams.net/

《TCP IP网络编程》第十八章

第 18 章 多线程服务器端的实现 18.1 理解线程的概念 线程背景&#xff1a; 第 10 章介绍了多进程服务端的实现方法。多进程模型与 select 和 epoll 相比的确有自身的优点&#xff0c;但同时也有问题。如前所述&#xff0c;创建&#xff08;复制&#xff09;进程的工作本身会…

【leetcode 力扣刷题】旋转矩阵(循环过程边界控制)

力扣刷题 旋转矩阵 二维矩阵按圈遍历&#xff08;顺时针 or 逆时针&#xff09;遍历59. 旋转矩阵Ⅱ54. 旋转矩阵剑指 Offer 29. 顺时针打印矩阵 二维矩阵按圈遍历&#xff08;顺时针 or 逆时针&#xff09;遍历 下面的题目的主要考察点都是&#xff0c;二维数组从左上角开始顺…

【Nginx18】Nginx学习:WebDav文件存储与图片媒体处理模块

Nginx学习&#xff1a;WebDav文件存储与图片媒体处理模块 今天的内容怎么说呢&#xff1f;有两个感觉非常有意思&#xff0c;另外一些就差点意思。有意思的是&#xff0c;咱们可以直接用 Nginx 的 Webdav 功能搭建一个网盘&#xff0c;另外也可以实现动态的图片处理。这两个功能…

MyBatis的入门级环境搭建及增删改查,详细易懂

目录 一.mybatis的简介 二.MyBatis的环境搭建 2.1 导入pom依赖 2.2 数据库文件导入连接 2.3 修改web.xml文件 2.4 安装插件 2.5 配置文件 2.5.1 mybatis.cfg.xml文件 2.5.2 generatorConfig.xml文件 2.6 最后测试生成代码 三.MyBatis的增删改查 3.1 写service类&#xff…