IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 神经网络与深度学习day05---前馈神经网络 -> 正文阅读

[人工智能]神经网络与深度学习day05---前馈神经网络


前言

神经网络是由神经元按照一定的连接结构组合而成的网络。神经网络可以看作一个函数,通过简单非线性函数的多次复合,实现输入空间到输出空间的复杂映射 。 前馈神经网络是最早发明的简单人工神经网络。整个网络中的信息单向传播,可以用一个有向无环路图表示,这种网络结构简单,易于实现。
本文基于Jupyter来进行解释,用torch框架复述前馈神经网络的一些问题。


一、神经元简介

神经网络的基本组成单元为带有非线性激活函数的神经元,其结构如如图4.2所示。神经元是对生物神经元的结构和特性的一种简化建模,接收一组输入信号并产生输出。
在这里插入图片描述

1.1 净活性值

假设一个神经元接收的输入为 x ∈ R D \mathbf{x}\in \mathbb{R}^D xRD,其权重向量为 w ∈ R D \mathbf{w}\in \mathbb{R}^D wRD,神经元所获得的输入信号,即净活性值 z z z的计算方法为
z = w T x + b z =\mathbf{w}^T\mathbf{x}+b z=wTx+b
我们使用torch计算一组输入的净活性值。代码实现如下:

import torch

# 2个特征数为5的样本
X = torch.rand([2, 5])

# 含有5个参数的权重向量
w = torch.rand([5, 1])
# 偏置项
b = torch.rand([1, 1])

# 使用'torch.matmul'实现矩阵相乘
z = torch.matmul(X, w) + b
print("input X:", X)
print("weight w:", w, "\nbias b:", b)
print("output z:", z)

实现效果:
在这里插入图片描述

1.2 激活函数

1.2.1 Sigmoid函数和Tanh函数

Sigmoid 型函数是指一类S型曲线函数,为两端饱和函数。常用的 Sigmoid 型函数有 Logistic 函数和 Tanh 函数,其数学表达式为

Logistic 函数:

σ ( z ) = 1 1 + exp ? ( ? z ) 。( 4.4 ) \sigma(z) = \frac{1}{1+\exp(-z)}。(4.4) σ(z)=1+exp(?z)1?。(4.4

Tanh 函数:

t a n h ( z ) = exp ? ( z ) ? exp ? ( ? z ) exp ? ( z ) + exp ? ( ? z ) 。( 4.5 ) \mathrm{tanh}(z) = \frac{\exp(z)-\exp(-z)}{\exp(z)+\exp(-z)}。(4.5) tanh(z)=exp(z)+exp(?z)exp(z)?exp(?z)?。(4.5

Logistic函数和Tanh函数的代码实现和可视化如下:
代码:

%matplotlib inline
import matplotlib.pyplot as plt

# Logistic函数
def logistic(z):
    return 1.0 / (1.0 + torch.exp(-z))

# Tanh函数
def tanh(z):
    return (torch.exp(z) - torch.exp(-z)) / (torch.exp(z) + torch.exp(-z))

# 在[-10,10]的范围内生成10000个输入值,用于绘制函数曲线
z = torch.linspace(-10, 10, 10000)

plt.figure()
plt.plot(z.tolist(), logistic(z).tolist(), color='#e4007f', label="Logistic Function")
plt.plot(z.tolist(), tanh(z).tolist(), color='#f19ec2', linestyle ='--', label="Tanh Function")

ax = plt.gca() # 获取轴,默认有4个
# 隐藏两个轴,通过把颜色设置成none
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
# 调整坐标轴位置   
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
plt.legend(loc='lower right', fontsize='large')

plt.savefig('fw-logistic-tanh.pdf')
plt.show()

可视化结果:
在这里插入图片描述

1.2.2 ReLU 函数

常见的ReLU函数有ReLU和带泄露的ReLU(Leaky ReLU),数学表达式分别为:

R e L U ( z ) = max ? ( 0 , z ) , ( 4.6 ) \mathrm{ReLU}(z) = \max(0,z),(4.6) ReLU(z)=max(0,z),4.6

L e a k y R e L U ( z ) = max ? ( 0 , z ) + λ min ? ( 0 , z ) , ( 4.7 ) \mathrm{LeakyReLU}(z) = \max(0,z)+\lambda \min(0,z),(4.7) LeakyReLU(z)=max(0,z)+λmin(0,z),4.7

其中 λ \lambda λ为超参数。

可视化ReLU和带泄露的ReLU的函数的代码实现和可视化如下:

# ReLU
def relu(z):
    return torch.maximum(z, torch.tensor(0.))

# 带泄露的ReLU
def leaky_relu(z, negative_slope=0.1):
    # 当前版本torch暂不支持直接将bool类型转成int类型,因此调用了torch的cast函数来进行显式转换
    a1 = (torch.can_cast((z > 0).dtype, to=torch.float32) * z) 
    a2 = (torch.can_cast((z <= 0).dtype, to=torch.float32) * (negative_slope * z))
    return a1 + a2

# 在[-10,10]的范围内生成一系列的输入值,用于绘制relu、leaky_relu的函数曲线
z = torch.linspace(-10, 10, 10000)

plt.figure()
plt.plot(z.tolist(), relu(z).tolist(), color="#e4007f", label="ReLU Function")
plt.plot(z.tolist(), leaky_relu(z).tolist(), color="#f19ec2", linestyle="--", label="LeakyReLU Function")

ax = plt.gca()
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
plt.legend(loc='upper left', fontsize='large')
plt.savefig('fw-relu-leakyrelu.pdf')
plt.show()

可视化结果:
在这里插入图片描述

1.2.3(选做)其他函数

二、基于前馈神经网络的二分类任务


图4.3: 前馈神经网络结构

2.1 数据集构建

我们使用带噪声的弯月数据集进行数据集的构建。

from nndl.dataset import make_moons

# 采样1000个样本
n_samples = 1000
X, y = make_moons(n_samples=n_samples, shuffle=True, noise=0.5)

num_train = 640
num_dev = 160
num_test = 200

X_train, y_train = X[:num_train], y[:num_train]
X_dev, y_dev = X[num_train:num_train + num_dev], y[num_train:num_train + num_dev]
X_test, y_test = X[num_train + num_dev:], y[num_train + num_dev:]

y_train = y_train.reshape([-1,1])
y_dev = y_dev.reshape([-1,1])
y_test = y_test.reshape([-1,1])

打印相关结果,证明数据已经生成:
在这里插入图片描述

2.2 模型构建

在实践中,为了提高模型的处理效率,通常将 N N N个样本归为一组进行成批地计算。假设网络第 l l l层的输入为 A ( l ? 1 ) ∈ R N × M l ? 1 \boldsymbol{A}^{(l-1)}\in \mathbb{R}^{N\times M_{l-1}} A(l?1)RN×Ml?1?,其中每一行为一个样本,则前馈网络中第 l l l层的计算公式为

Z ( l ) = A ( l ? 1 ) W ( l ) + b ( l ) ∈ R N × M l , ( 4.8 ) \mathbf Z^{(l)}=\mathbf A^{(l-1)} \mathbf W^{(l)} +\mathbf b^{(l)} \in \mathbb{R}^{N\times M_{l}}, (4.8) Z(l)=A(l?1)W(l)+b(l)RN×Ml?,(4.8)
A ( l ) = f l ( Z ( l ) ) ∈ R N × M l , ( 4.9 ) \mathbf A^{(l)}=f_l(\mathbf Z^{(l)}) \in \mathbb{R}^{N\times M_{l}}, (4.9) A(l)=fl?(Z(l))RN×Ml?,(4.9)
其中 Z ( l ) \mathbf Z^{(l)} Z(l) N N N个样本第 l l l层神经元的净活性值, A ( l ) \mathbf A^{(l)} A(l) N N N个样本第 l l l层神经元的活性值, W ( l ) ∈ R M l ? 1 × M l \boldsymbol{W}^{(l)}\in \mathbb{R}^{M_{l-1}\times M_{l}} W(l)RMl?1?×Ml?为第 l l l层的权重矩阵, b ( l ) ∈ R 1 × M l \boldsymbol{b}^{(l)}\in \mathbb{R}^{1\times M_{l}} b(l)R1×Ml?为第 l l l层的偏置。

2.2.1 线性层算子

from nndl.op import Op

# 实现线性层算子
class Linear(Op):
    def __init__(self, input_size, output_size, name, weight_init=torch.normal, bias_init=torch.zeros):
        """
        输入:
            - input_size:输入数据维度
            - output_size:输出数据维度
            - name:算子名称
            - weight_init:权重初始化方式,默认使用'torch.standard_normal'进行标准正态分布初始化
            - bias_init:偏置初始化方式,默认使用全0初始化
        """
        
        self.params = {}
        # 初始化权重
        self.params['W'] = weight_init(0,1,[input_size,output_size])
        # 初始化偏置
        self.params['b'] = bias_init([1,output_size])
        self.inputs = None

        self.name = name

    def forward(self, inputs):
        """
        输入:
            - inputs:shape=[N,input_size], N是样本数量
        输出:
            - outputs:预测值,shape=[N,output_size]
        """
        self.inputs = inputs

        outputs = torch.matmul(self.inputs, self.params['W']) + self.params['b']
        return outputs

2.2.2 Logistic算子(激活函数)

class Logistic(Op):
    def __init__(self):
        self.inputs = None
        self.outputs = None

    def forward(self, inputs):
        """
        输入:
            - inputs: shape=[N,D]
        输出:
            - outputs:shape=[N,D]
        """
        outputs = 1.0 / (1.0 + torch.exp(-inputs))
        self.outputs = outputs
        return outputs

2.2.3 层的串行组合

# 实现一个两层前馈神经网络
class Model_MLP_L2(Op):
    def __init__(self, input_size, hidden_size, output_size):
        """
        输入:
            - input_size:输入维度
            - hidden_size:隐藏层神经元数量
            - output_size:输出维度
        """
        self.fc1 = Linear(input_size, hidden_size, name="fc1")
        self.act_fn1 = Logistic()
        self.fc2 = Linear(hidden_size, output_size, name="fc2")
        self.act_fn2 = Logistic()

    def __call__(self, X):
        return self.forward(X)

    def forward(self, X):
        """
        输入:
            - X:shape=[N,input_size], N是样本数量
        输出:
            - a2:预测值,shape=[N,output_size]
        """
        z1 = self.fc1(X)
        a1 = self.act_fn1(z1)
        z2 = self.fc2(a1)
        a2 = self.act_fn2(z2)
        return a2

简单测试一下:

# 实例化模型
model = Model_MLP_L2(input_size=5, hidden_size=10, output_size=1)
# 随机生成1条长度为5的数据
X = torch.rand([1, 5])
result = model(X)
print ("result: ", result)

测试结果:
在这里插入图片描述

2.3 损失函数

我们采用交叉熵损失,什么是交叉熵?具体详见博客:

交叉熵损失函数

2.4 模型优化

神经网络的参数主要是通过梯度下降法进行优化的,因此需要计算最终损失对每个参数的梯度。 由于神经网络的层数通常比较深,其梯度计算和上一章中的线性分类模型的不同的点在于:线性模型通常比较简单可以直接计算梯度,而神经网络相当于一个复合函数,需要利用链式法则进行反向传播来计算梯度。

2.4.1 反向传播算法和损失函数

二分类交叉熵损失函数对神经网络的输出 y ^ \hat{\boldsymbol{y}} y^?的偏导数为:
? R ? y ^ = ? 1 N ( d i a l o g ( 1 y ^ ) y ? d i a l o g ( 1 1 ? y ^ ) ( 1 ? y ) ) ( 4.10 ) = ? 1 N ( 1 y ^ ⊙ y ? 1 1 ? y ^ ⊙ ( 1 ? y ) ) , ( 4.11 ) \frac{\partial R}{\partial \hat{\boldsymbol{y}}} = -\frac{1}{N}(\mathrm{dialog}(\frac{1}{\hat{\boldsymbol{y}}})\boldsymbol{y}-\mathrm{dialog}(\frac{1}{1-\hat{\boldsymbol{y}}})(1-\boldsymbol{y})) (4.10) \\ = -\frac{1}{N}(\frac{1}{\hat{\boldsymbol{y}}}\odot\boldsymbol{y}-\frac{1}{1-\hat{\boldsymbol{y}}}\odot(1-\boldsymbol{y})), (4.11) ?y^??R?=?N1?(dialog(y^?1?)y?dialog(1?y^?1?)(1?y))(4.10)=?N1?(y^?1?y?1?y^?1?(1?y)),(4.11)
其中 d i a l o g ( x ) dialog(\boldsymbol{x}) dialog(x)表示以向量 x \boldsymbol{x} x为对角元素的对角阵, 1 x = 1 x 1 , . . . , 1 x N \frac{1}{\boldsymbol{x}}=\frac{1}{x_1},...,\frac{1}{x_N} x1?=x1?1?,...,xN?1?表示逐元素除, ⊙ \odot 表示逐元素积。

# 实现交叉熵损失函数
class BinaryCrossEntropyLoss(Op):
    def __init__(self, model):
        self.predicts = None
        self.labels = None
        self.num = None

        self.model = model

    def __call__(self, predicts, labels):
        return self.forward(predicts, labels)

    def forward(self, predicts, labels):
        """
        输入:
            - predicts:预测值,shape=[N, 1],N为样本数量
            - labels:真实标签,shape=[N, 1]
        输出:
            - 损失值:shape=[1]
        """
        self.predicts = predicts
        self.labels = labels
        self.num = self.predicts.shape[0]
        loss = -1. / self.num * (torch.matmul(self.labels.t(), torch.log(self.predicts)) 
                + torch.matmul((1-self.labels.t()), torch.log(1-self.predicts)))

        loss = torch.squeeze(loss, axis=1)
        return loss

    def backward(self):
        # 计算损失函数对模型预测的导数
        loss_grad_predicts = -1.0 * (self.labels / self.predicts - 
                       (1 - self.labels) / (1 - self.predicts)) / self.num
        
        # 梯度反向传播
        self.model.backward(loss_grad_predicts)

2.4.2 Logistic算子

由于Logistic函数中没有参数,这里不需要在backward()方法中计算该算子参数的梯度。

class Logistic(Op):
    def __init__(self):
        self.inputs = None
        self.outputs = None
        self.params = None

    def forward(self, inputs):
        outputs = 1.0 / (1.0 + torch.exp(-inputs))
        self.outputs = outputs
        return outputs

    def backward(self, grads):
        # 计算Logistic激活函数对输入的导数
        outputs_grad_inputs = torch.multiply(self.outputs, (1.0 - self.outputs))
        return torch.multiply(grads,outputs_grad_inputs)

2.4.3 线性层

计算线性层参数的梯度 由于线性层算子中包含有可学习的参数𝑾和𝒃,因此backward()除了实现梯度反传外,还需要计算算子内部的参数的梯度

class Linear(Op):
    def __init__(self, input_size, output_size, name, weight_init=torch.normal, bias_init=torch.zeros):
        self.params = {}
        self.params['W'] = weight_init(0,1,[input_size, output_size])
        self.params['b'] = bias_init([1, output_size])

        self.inputs = None
        self.grads = {}

        self.name = name

    def forward(self, inputs):
        self.inputs = inputs
        outputs = torch.matmul(self.inputs, self.params['W']) + self.params['b']
        return outputs

    def backward(self, grads):
        """
        输入:
            - grads:损失函数对当前层输出的导数
        输出:
            - 损失函数对当前层输入的导数
        """
        self.grads['W'] = torch.matmul(self.inputs.T, grads)
        self.grads['b'] = torch.sum(grads, axis=0)

        # 线性层输入的梯度
        return torch.matmul(grads, self.params['W'].T)

2.4.4 整个网络

实现完整的两层神经网络的前向和反向计算。代码实现如下

class Model_MLP_L2(Op):
    def __init__(self, input_size, hidden_size, output_size):
        # 线性层
        self.fc1 = Linear(input_size, hidden_size, name="fc1")
        # Logistic激活函数层
        self.act_fn1 = Logistic()
        self.fc2 = Linear(hidden_size, output_size, name="fc2")
        self.act_fn2 = Logistic()

        self.layers = [self.fc1, self.act_fn1, self.fc2, self.act_fn2]

    def __call__(self, X):
        return self.forward(X)

    # 前向计算
    def forward(self, X):
        z1 = self.fc1(X)
        a1 = self.act_fn1(z1)
        z2 = self.fc2(a1)
        a2 = self.act_fn2(z2)
        return a2
        
    # 反向计算
    def backward(self, loss_grad_a2):
        loss_grad_z2 = self.act_fn2.backward(loss_grad_a2)
        loss_grad_a1 = self.fc2.backward(loss_grad_z2)
        loss_grad_z1 = self.act_fn1.backward(loss_grad_a1)
        loss_grad_inputs = self.fc1.backward(loss_grad_z1)

2.4.5 优化器

在计算好神经网络参数的梯度之后,我们将梯度下降法中参数的更新过程实现在优化器中。

from abc import abstractmethod
class Optimizer(object):
    def __init__(self, init_lr, model):
        """
        优化器类初始化
        """
        #初始化学习率,用于参数更新的计算
        self.init_lr = init_lr
        #指定优化器需要优化的模型
        self.model = model

    @abstractmethod
    def step(self):
        """
        定义每次迭代如何更新参数
        """
        pass

class BatchGD(Optimizer):
    def __init__(self, init_lr, model):
        super(BatchGD, self).__init__(init_lr=init_lr, model=model)

    def step(self):
        # 参数更新
        for layer in self.model.layers: # 遍历所有层
            if isinstance(layer.params, dict):
                for key in layer.params.keys():
                    layer.params[key] = layer.params[key] - self.init_lr * layer.grads[key]

2.5 完善Runner类:RunnerV2_1

基于3.1.6实现的 RunnerV2 类主要针对比较简单的模型。而在本章中,模型由多个算子组合而成,通常比较复杂,因此本节继续完善并实现一个改进版: RunnerV2_1类,其主要加入的功能有:

支持自定义算子的梯度计算,在训练过程中调用self.loss_fn.backward()从损失函数开始反向计算梯度;
每层的模型保存和加载,将每一层的参数分别进行保存和加载。

import os
os.getcwd()
class RunnerV2_1(object):
    def __init__(self, model, optimizer, metric, loss_fn, **kwargs):
        self.model = model
        self.optimizer = optimizer
        self.loss_fn = loss_fn
        self.metric = metric

        # 记录训练过程中的评估指标变化情况
        self.train_scores = []
        self.dev_scores = []

        # 记录训练过程中的评价指标变化情况
        self.train_loss = []
        self.dev_loss = []

    def train(self, train_set, dev_set, **kwargs):
        # 传入训练轮数,如果没有传入值则默认为0
        num_epochs = kwargs.get("num_epochs", 0)
        # 传入log打印频率,如果没有传入值则默认为100
        log_epochs = kwargs.get("log_epochs", 100)

        # 传入模型保存路径
        save_dir = kwargs.get("save_dir", None)
        
        # 记录全局最优指标
        best_score = 0
        # 进行num_epochs轮训练
        for epoch in range(num_epochs):
            X, y = train_set
            # 获取模型预测
            logits = self.model(X)
            # 计算交叉熵损失
            trn_loss = self.loss_fn(logits, y) # return a tensor
            
            self.train_loss.append(trn_loss.item())
            # 计算评估指标
            trn_score = self.metric(logits, y).item()
            self.train_scores.append(trn_score)

            self.loss_fn.backward()

            # 参数更新
            self.optimizer.step()
            dev_score, dev_loss = self.evaluate(dev_set)
            # 如果当前指标为最优指标,保存该模型
            if dev_score > best_score:
                print(f"[Evaluate] best accuracy performence has been updated: {best_score:.5f} --> {dev_score:.5f}")
                best_score = dev_score
                if save_dir:
                    self.save_model(save_dir)

            if log_epochs and epoch % log_epochs == 0:
                print(f"[Train] epoch: {epoch}/{num_epochs}, loss: {trn_loss.item()}")
                
    def evaluate(self, data_set):
        X, y = data_set
        # 计算模型输出
        logits = self.model(X)
        # 计算损失函数
        loss = self.loss_fn(logits, y).item()
        self.dev_loss.append(loss)
        # 计算评估指标
        score = self.metric(logits, y).item()
        self.dev_scores.append(score)
        return score, loss
    
    def predict(self, X):
        return self.model(X)

    def save_model(self, save_dir):
        # 对模型每层参数分别进行保存,保存文件名称与该层名称相同
        for layer in self.model.layers: # 遍历所有层
            if isinstance(layer.params, dict):
                torch.save(layer.params, os.path.join(save_dir, layer.name+".pt"))

    def load_model(self, model_dir):
        # 获取所有层参数名称和保存路径之间的对应关系
        model_file_names = os.listdir(model_dir)
        name_file_dict = {}
        for file_name in model_file_names:
            name = file_name.replace(".pt","")
            name_file_dict[name] = os.path.join(model_dir, file_name)

        # 加载每层参数
        for layer in self.model.layers: # 遍历所有层
            if isinstance(layer.params, dict):
                name = layer.name
                file_path = name_file_dict[name]
                layer.params = torch.load(file_path)

2.6 模型训练

基于RunnerV2_1,使用训练集和验证集进行模型训练,共训练2000个epoch。评价指标为第章介绍的accuracy。代码实现如下:

import os
os.getcwd()
#3.1.5评价指标
def accuracy(preds, labels):
    """
    输入:
        - preds:预测值,二分类时,shape=[N, 1],N为样本数量,多分类时,shape=[N, C],C为类别数量
        - labels:真实标签,shape=[N, 1]
    输出:
        - 准确率:shape=[1]
    """
    print(preds)
    # 判断是二分类任务还是多分类任务,preds.shape[1]=1时为二分类任务,preds.shape[1]>1时为多分类任务
    if preds.shape[1] == 1:
        # 二分类时,判断每个概率值是否大于0.5,当大于0.5时,类别为1,否则类别为0
        # 使用'torch.can_cast'将preds的数据类型转换为float32类型
        preds = torch.can_cast((preds>=0.5).dtype,to=torch.float32)
    else:
        # 多分类时,使用'torch.argmax'计算最大元素索引作为类别
        preds = torch.argmax(preds,dim=1)
        torch.can_cast(preds.dtype,torch.int32)
    return torch.mean(torch.as_tensor((preds == labels), dtype=torch.float32))

torch.manual_seed(123)
epoch_num = 1000
model_saved_dir = "D:\\model"

# 输入层维度为2
input_size = 2
# 隐藏层维度为5
hidden_size = 5
# 输出层维度为1
output_size = 1

# 定义网络
model = Model_MLP_L2(input_size=input_size, hidden_size=hidden_size, output_size=output_size)

# 损失函数
loss_fn = BinaryCrossEntropyLoss(model)

# 优化器
learning_rate = 2.0
optimizer = BatchGD(learning_rate, model)

# 评价方法
metric = accuracy

# 实例化RunnerV2_1类,并传入训练配置
runner = RunnerV2_1(model, optimizer, metric, loss_fn)

runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=epoch_num, log_epochs=50, save_dir=model_saved_dir)

结果:
在这里插入图片描述
可视化观察训练集与验证集的损失函数变化情况。

print(runner.train_loss)
# 打印训练集和验证集的损失
plt.figure()
plt.plot(range(epoch_num), runner.train_loss, color="#e4007f", label="Train loss")
plt.plot(range(epoch_num), runner.dev_loss, color="#f19ec2", linestyle='--', label="Dev loss")
plt.xlabel("epoch", fontsize='large')
plt.ylabel("loss", fontsize='large')
plt.legend(fontsize='x-large')
plt.savefig('fw-loss2.pdf')
plt.show()

模型训练结果:
在这里插入图片描述

2.7 性能评价

# 加载训练好的模型
runner.load_model(model_saved_dir)
# 在测试集上对模型进行评价
score, loss = runner.evaluate([X_test, y_test])

print("[Test] score/loss: {:.4f}/{:.4f}".format(score, loss))

评价结果:

在这里插入图片描述
我们对结果进行可视化:

import math

# 均匀生成40000个数据点
x1, x2 = torch.meshgrid(torch.linspace(-math.pi, math.pi, 200), torch.linspace(-math.pi, math.pi, 200))
x = torch.stack([torch.flatten(x1), torch.flatten(x2)], axis=1)

# 预测对应类别
y = runner.predict(x)
y = torch.squeeze(torch.as_tensor((y>=0.5),dtype=torch.float32),dim=-1)

# 绘制类别区域
plt.ylabel('x2')
plt.xlabel('x1')
plt.scatter(x[:,0].tolist(), x[:,1].tolist(), c=y.tolist(), cmap=plt.cm.Spectral)

plt.scatter(X_train[:, 0].tolist(), X_train[:, 1].tolist(), marker='*', c=torch.squeeze(y_train,axis=-1).tolist())
plt.scatter(X_dev[:, 0].tolist(), X_dev[:, 1].tolist(), marker='*', c=torch.squeeze(y_dev,axis=-1).tolist())
plt.scatter(X_test[:, 0].tolist(), X_test[:, 1].tolist(), marker='*', c=torch.squeeze(y_test,axis=-1).tolist())

可视化结果:
在这里插入图片描述

解决问题

1、加权求和与仿射变换之间有什么区别和联系?
在这里插入图片描述
在这里插入图片描述
我的理解:
仿射变换和加权求和,加权求和本质上是一个线性变换,而放射变换呢,简单来说就是线性变换+平移。通过平移,一个向量空间可以进入到另一个向量空间进行计算。
线性变换有三个特点:
①变换前是直线,变换后依然是直线;
②直线比例保持不变
③变换前是原点,变换后依然是原点
仿射变换有两个特点:
①变换前是直线,变换后依然是直线;
②直线比例保持不变
通过上述表述,我们或多或少都能看出,仿射变换和线性变换(本题来说是加权求和)的区别。
联系:仿射变换按我的理解来说,在本题中应该是加权求和之后再进行平移。
2.、对于下列的实验过程,谈谈你的思考
对比:
? 3.1 基于Logistic回归的二分类任务
? 4.2 基于前馈神经网络的二分类任务
我的理解:
先说一下区别:
Logistic回归类似于一个单层的神经网络,有N个输入的情况下只有一个输出,只能处理一个线性可分的问题,是一个线性模型
前馈神经网络却是有很多个网络层构成,每个层都有好多个神经元,对于隐藏层中的每个单元本身都是一个逼近Logistic回归的过程,能够处理非线性的问题,现实生活中大多数的问题都是线性不可分的,是一个非线性模型。
神经网络和前面的 logistic 回归相比,神经网络因为有了激活函数的存在,成了一个非线性分类器,所以神经网络分类的边界更加复杂。
有人说用前馈神经网络逼近Logistic回归的,我看到了他的意思应该是用一层的神经网络去逼近Logistic回归,说的也很有道理。
在这里插入图片描述
相同点的话,就是都使用了交叉熵损失作为损失函数。其他的就是Logistic回归就是一个单层的神经网络吧。

参考博客:

仿射变换
线型回归、逻辑回归和神经网络的区别
前馈神经网络和Logit回归的比较研究
NNDL 实验4(上)
自动微分-动手学深度学习
前向传播、反向传播和计算图
通过例子来感受神经网络的优越 (logistic 回归 vs 神经网络)

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:52:59  更:2022-09-30 00:53:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:55:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码