• 机器学习——多层感知机


    感知机

    缺点:只能处理线性问题,感知机无法解决异或问题
    在这里插入图片描述
    在这里偏置就像线性模型的常数项,加入偏置模型的表达能力增强,而激活函数就像示性函数,可以模拟神经元的兴奋和抑制,当大于等于0就输出1。
    在这里插入图片描述

    多层感知机MLP

    前馈网络的层数是指权重的层数
    多个单层感知机按前馈结构,前馈结构就是层只与相邻层连接,不跨越连接,就是多层感知机

    激活函数
    逻辑斯蒂函数
    双曲正切函数
    线性整流单元ReLU

    一般让所有的隐含层的激活函数相同,输出层的激活函数需根据任务的需求选择,二分类可以选择逻辑斯蒂回归,多分类用softmax函数
    MLP相比单层感知机的表达能力提升,关键在于非线性激活函数
    可以证明任意一个R上的连续函数都可以由MLP来拟合,而对其非线性的激活函数的形式要求很少,也称作普适逼近定理
    非线性对提升模型的表达能力很重要,其实因为非线性变换相当于提升了数据的维度,维度提升的好处就在于低维数据不可分的问题可以在高维中可分

    import torch # PyTorch库
    import torch.nn as nn # PyTorch中与神经网络相关的工具
    from torch.nn.init import normal_ # 正态分布初始化
    
    torch_activation_dict = {
        'identity': lambda x: x,
        'sigmoid': torch.sigmoid,
        'tanh': torch.tanh,
        'relu': torch.relu
    }
    
    # 定义MLP类,基于PyTorch的自定义模块通常都继承nn.Module
    # 继承后,只需要实现forward函数,进行前向传播
    # 反向传播与梯度计算均由PyTorch自动完成
    class MLP_torch(nn.Module):
    
        def __init__(
            self, 
            layer_sizes, # 包含每层大小的list
            use_bias=True, 
            activation='relu',
            out_activation='identity'
        ):
            super().__init__() # 初始化父类
            self.activation = torch_activation_dict[activation]
            self.out_activation = torch_activation_dict[out_activation]
            self.layers = nn.ModuleList() # ModuleList以列表方式存储PyTorch模块
            num_in = layer_sizes[0]
            for num_out in layer_sizes[1:]:
                # 创建全连接层
                self.layers.append(nn.Linear(num_in, num_out, bias=use_bias))
                # 正态分布初始化,采用与前面手动实现时相同的方式
                normal_(self.layers[-1].weight, std=1.0)
                # 偏置项为全0
                self.layers[-1].bias.data.fill_(0.0)
                num_in = num_out
    
        def forward(self, x):
            # 前向传播
            # PyTorch可以自行处理batch_size等维度问题
            # 我们只需要让输入依次通过每一层即可
            for i in range(len(self.layers) - 1):
                x = self.layers[i](x)
                x = self.activation(x)
            # 输出层
            x = self.layers[-1](x)
            x = self.out_activation(x)
            return x
    #%%
    # 设置超参数
    num_epochs = 1000
    learning_rate = 0.1
    batch_size = 128
    eps = 1e-7
    torch.manual_seed(0)
    
    # 初始化MLP模型
    mlp = MLP_torch(layer_sizes=[2, 4, 1], use_bias=True, 
        out_activation='sigmoid')
    
    # 定义SGD优化器
    opt = torch.optim.SGD(mlp.parameters(), lr=learning_rate)
    
    # 训练过程
    losses = []
    test_losses = []
    test_accs = []
    for epoch in range(num_epochs):
        st = 0
        loss = []
        while True:
            ed = min(st + batch_size, len(x_train))
            if st >= ed:
                break
            # 取出batch,转为张量
            x = torch.tensor(x_train[st: ed], 
                dtype=torch.float32)
            y = torch.tensor(y_train[st: ed], 
                dtype=torch.float32).reshape(-1, 1)
            # 计算MLP的预测
            # 调用模型时,PyTorch会自动调用模型的forward方法
            # y_pred的维度为(batch_size, layer_sizes[-1])
            y_pred = mlp(x)
            # 计算交叉熵损失
            train_loss = torch.mean(-y * torch.log(y_pred + eps) \
                - (1 - y) * torch.log(1 - y_pred + eps))
            # 清空梯度
            opt.zero_grad()
            # 反向传播
            train_loss.backward()
            # 更新参数
            opt.step()
    
            # 记录累加损失,需要先将损失从张量转为numpy格式
            loss.append(train_loss.detach().numpy())
            st += batch_size
    
        losses.append(np.mean(loss))
        # 计算测试集上的交叉熵
        # 在不需要梯度的部分,可以用torch.inference_mode()加速计算
        with torch.inference_mode():
            x = torch.tensor(x_test, dtype=torch.float32)
            y = torch.tensor(y_test, dtype=torch.float32).reshape(-1, 1)
            y_pred = mlp(x)
            test_loss = torch.sum(-y * torch.log(y_pred + eps) \
                - (1 - y) * torch.log(1 - y_pred + eps)) / len(x_test)
            test_acc = torch.sum(torch.round(y_pred) == y) / len(x_test)
            test_losses.append(test_loss.detach().numpy())
            test_accs.append(test_acc.detach().numpy())
    
    print('测试精度:', test_accs[-1])
    # 将损失变化进行可视化
    plt.figure(figsize=(16, 6))
    plt.subplot(121)
    plt.plot(losses, color='blue', label='train loss')
    plt.plot(test_losses, color='red', ls='--', label='test loss')
    plt.xlabel('Step')
    plt.ylabel('Loss')
    plt.title('Cross-Entropy Loss')
    plt.legend()
    
    plt.subplot(122)
    plt.plot(test_accs, color='red')
    plt.ylim(top=1.0)
    plt.xlabel('Step')
    plt.ylabel('Accuracy')
    plt.title('Test Accuracy')
    plt.show()
    
  • 相关阅读:
    ubuntu生成pem证书连接服务器(已验证)
    js实现页面元素的拖拽
    美国护肤品公司【Elevai Labs】申请750万美元纳斯达克IPO上市
    2022/6/27 Quartz(定时任务)讲解+入门案例
    网络工程师怎么才算开窍
    【Linux】环境基础开发工具使用
    解决聊天窗口的输入框高度变化,引起中间滚动内容的跳动问题
    LVGL-TLSF内存管理算法-TLSF_LOG2_CEIL(n)宏详解:计算内存块所属内存池类别
    消息中间件rabbitmq
    Linux内存管理(二十二):页面回收简介和 kswapd(1)
  • 原文地址:https://blog.csdn.net/star_and_sun/article/details/139425908