ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

从零实现深度学习框架——动手实现Softmax回归

2022-01-18 19:31:47  阅读:354  来源: 互联网

标签:Loss target 框架 iris 实现 Train Softmax test Tensor


引言

本着“凡我不能创造的,我就不能理解”的思想,本系列文章会基于纯Python以及NumPy从零创建自己的深度学习框架,该框架类似PyTorch能实现自动求导。

要深入理解深度学习,从零开始创建的经验非常重要,从自己可以理解的角度出发,尽量不适用外部完备的框架前提下,实现我们想要的模型。本系列文章的宗旨就是通过这样的过程,让大家切实掌握深度学习底层实现,而不是仅做一个调包侠。
本系列文章首发于微信公众号:JavaNLP

关注公众号

上篇文章对Softmax回归进行了简单的介绍,本文我们就来从零实现Softmax回归。

实现Softmax函数

首先我们实现Softmax回归的灵魂:

def softmax(x, axis=-1):
    y = x.exp()
    return y / y.sum(axis=axis, keepdims=True)

softmax ( z i ) = exp ⁡ ( z i ) ∑ j = 1 K exp ⁡ ( z j ) 1 ≤ i ≤ K (1) \text{softmax}(z_i) = \frac{\exp(z_i)}{\sum_{j=1}^K \exp(z_j)} \quad 1 \leq i \leq K \tag{1} softmax(zi​)=∑j=1K​exp(zj​)exp(zi​)​1≤i≤K(1)

实现交叉熵损失函数

L C E ( y ^ , y ) = − ∑ k = 1 K y k log ⁡ y ^ k (2) L_{CE} (\hat y ,y) = - \sum_{k=1}^K y_k \log \hat y_k \tag 2 LCE​(y^​,y)=−k=1∑K​yk​logy^​k​(2)

def cross_entropy(input: Tensor, target: Tensor, reduction: str = "mean") -> Tensor:
    N = len(target)

    p = softmax(input)

    errors = - target * p.log()
    # errors = - p[np.arange(N), target.data].log()

    if reduction == "mean":
        loss = errors.sum() / N
    elif reduction == "sum":
        loss = errors.sum()
    else:
        loss = errors
    return loss

这里调用刚才实现的softmax函数把输入input转换成概率,所以这里的输入实际上是logits,即未经过Softmax的 z z z值。

然后我们基于此实现损失类:

class CrossEntropyLoss(_Loss):
    def __init__(self, reduction: str = "mean") -> None:
        super().__init__(reduction)

    def forward(self, input: Tensor, target: Tensor) -> Tensor:
        return F.cross_entropy(input, target, self.reduction)

实现Softmax回归

class SoftmaxRegression(Module):
    def __init__(self, input_dim, output_dim):
        self.linear = Linear(input_dim, output_dim)

    def forward(self, x: Tensor) -> Tensor:
        # 只要输出logits即可
        return self.linear(x)

这里只需要计算出 z z z的结果即可。

使用Softmax回归分类鸢尾花

我们加载sklearn中的iris数据集。

鸢尾花示例

鸢尾花如上所示,有4个特征:

  • Sepal.Length(花萼长度)

  • Sepal.Width(花萼宽度)

  • Petal.Length(花瓣长度)

  • Petal.Width(花瓣宽度)

有三个类别:Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),以及Iris Virginica(维吉尼亚鸢尾)。

为了可视化的方便,我们先只考虑两个特征,可视化结果如下:

iris数据集可视化

从上图可以看到,只考虑前两个特征的情况下,橙色点和绿色点看起来不太好分,这暂且不管,我们先写代码,硬Train一发。

def generate_dataset(draw_picture=False):
    iris = datasets.load_iris()

    X = iris['data'][:, :2]  # 我们只需要前两个特征
    y = iris['target']
    names = iris['target_names']  # 类名
    feature_names = iris['feature_names']  # 特征名

    if draw_picture:
        x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
        y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5

        plt.figure(2, figsize=(8, 6))
        plt.clf()

        for target, target_name in enumerate(names):
            X_plot = X[y == target]
            plt.plot(X_plot[:, 0], X_plot[:, 1],
                     linestyle='none',
                     marker='o',
                     label=target_name)
        plt.xlabel(feature_names[0])
        plt.ylabel(feature_names[1])
        plt.xlim(x_min, x_max)
        plt.ylim(y_min, y_max)

        plt.axis('equal')
        plt.legend()

        fig = plt.gcf()
        fig.savefig('iris.png', dpi=100)

    y = np.eye(3)[y]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=2)

    return Tensor(X_train), Tensor(X_test), Tensor(y_train), Tensor(y_test)


if __name__ == '__main__':
    X_train, X_test, y_train, y_test = generate_dataset(True)
    epochs = 2000

    model = SoftmaxRegression(2, 3)  # 2个特征 3个输出

    optimizer = SGD(model.parameters(), lr=1e-1)

    loss = CrossEntropyLoss()

    losses = []

    for epoch in range(int(epochs)):
        outputs = model(X_train)
        l = loss(outputs, y_train)
        optimizer.zero_grad()
        l.backward()
        optimizer.step()

        if (epoch + 1) % 20 == 0:
            losses.append(l.item())
            print(f"Train -  Loss: {l.item()}")

    # 在测试集上测试
    outputs = model(X_test)
    correct = np.sum(outputs.numpy().argmax(-1) == y_test.numpy().argmax(-1))
    accuracy = 100 * correct / len(y_test)
    print(f"Test Accuracy:{accuracy}")

为了验证泛化能力,我们这里还区分了训练集和测试集。

Train -  Loss: 0.9068448543548584
Train -  Loss: 0.8322725296020508
Train -  Loss: 0.7793639302253723
Train -  Loss: 0.740231454372406
...
Train -  Loss: 0.4532046616077423
Train -  Loss: 0.45260095596313477
Train -  Loss: 0.45200586318969727
Train -  Loss: 0.45141926407814026
Train -  Loss: 0.45084092020988464
Train -  Loss: 0.4502706527709961
Train -  Loss: 0.44970834255218506
Train -  Loss: 0.4491537809371948
Train -  Loss: 0.44860681891441345
Test Accuracy:76.66666666666667

如果我们考虑所有的特征准确率会不会很一点?

我们只要修改两行代码:

def generate_dataset(draw_picture=False):
    iris = datasets.load_iris()

    X = iris['data'] # 修改这里
    
# 修改模型的参数
model = SoftmaxRegression(4, 3)  # 4个特征 3个输出

再次训练查看结果:

Train -  Loss: 0.7530185580253601
Train -  Loss: 0.6372731328010559
Train -  Loss: 0.5648812055587769
Train -  Loss: 0.5048649907112122
Train -  Loss: 0.44937923550605774
Train -  Loss: 0.3961796164512634
Train -  Loss: 0.3457953631877899
Train -  Loss: 0.3021572232246399
Train -  Loss: 0.27336016297340393
...
Train -  Loss: 0.09917300194501877
Train -  Loss: 0.09881455451250076
Train -  Loss: 0.09846225380897522
Train -  Loss: 0.0981159582734108
Train -  Loss: 0.09777550399303436
Test Accuracy:100.0

啥也不说了。

完整代码

完整代码笔者上传到了程序员最大交友网站上去了,地址:

标签:Loss,target,框架,iris,实现,Train,Softmax,test,Tensor
来源: https://blog.csdn.net/yjw123456/article/details/122566618

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有