Softmax回归模型及其代码实现

一、Softamx回归模型的原理

        softmax回归跟线性回归一样,都是将输入特征与权重做线性叠加。与线性回归的一个主要不同在于,softmax回归的输出值个数等于标签里的类别数。比如线性回归中很常见的问题----预测房屋价格,我们通常会选取面积(平方米)和房龄(年)作为影响价格的两个因素,在这里面积和房龄作为特征而输出只有一个价格的预测值。而对于softmax,它更适用于做分类预测,比如在垃圾邮件分类问题中,我们往往需要选取几个主要的特征作为系统的输入,然后经过softmax分类算法得到邮件是垃圾邮件不是垃圾邮件概率值,它的输出是离散的值。

        现在我们具体看一下softmax模型是怎样得到的。假如我们需要对三类动物(狗、猫、鸡)进行分类,我们选取动物的四个特征作为输入,这时一共有4种特征和3种输出动物类别,所以权重包含12个标量(带下标的w)、偏差包含3个标量(带下标的b),且对每个输入计算o1,o2,o3这三个输出:

        图1.1用神经网络图描绘了上面的计算。 softmax回归同线性回归一样,也是一个单层神经网络。由于每个输出O1,O2,O3的计算都要依赖于所有的输入x1,x2,x3,x4,softmax回归的输出层也是一个全连接层。

图1.1 softmax回归是一个全连接层

        既然分类问题需要得到离散的预测输出,一个简单的办法是将输出值Oi当作预测类别是i的置信度,并将值最大的输出所对应的类作为模型的预测输出,即找到argmaxOi。例如,如果O1,O2,O3分别为0.1,10,0.1,由于O2最大,那么预测类别为2,为猫。

        然而,直接使⽤用输出层的输出有两个问题。一方面,由于输出层的输出值的范围不确定,我们难以直观上判断这些值的意义。例如,刚才举的例子中的输出值10表示“很置信”图像类别为猫,因为该输出值是其他两类的输出值的100倍。但如果O1 = O3 = 10*3 ,那么输出值10却又表示图像类别为猫的概率很低。另一方面,由于真实标签是离散值,这些离散值与不确定范围的输出值之间的误差难以衡量。

        softmax运算符(softmax operator)解决了以上两个问题。它通过下式将输出值变换成值为正且和为1的概率分布:

其中

         因此,我们便得到了一个合法的概率分布这时候我们便可以直接通过输出y_hat(y帽)的值进而预测的类别。

         因此softmax运算不不改变预测类别输出。

 二、Softmax回归的简单实现

         我们已经简单了解了softmax回归模型的原理,接下来,让我们一起用pytorch来实现一个softmax回归模型。首先导入所需的包和模块。 

import torch
from torch import nn
from torch.nn import init
import numpy as np
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l

         注:1.d2lzh_pytorch是需要自己去下载的,通过它可以直接调用写好的函数,方便编写程序。安装d2lzh_pytorch 包 - 简书 (jianshu.com)

                2.torch.nn仅支持输入一个batch的样本不支持单个样本输入,如果只有单个样本可用input.unsqueeze(0)来添加一维。

         获取和读取数据。代码使用了Fashion-MNIST数据集,,设置每次读取的批量大小。

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

        softmax回归的输出层是一个全连接层,所以我们用一个线性模块就可以了。因选取的每个batch样本x的形状为(batch_size,1,28,28),所以用view()将x的形状转换成(batch_size,784)后,再送入全连接层。

num_inputs = 784
num_outputs = 10
class LinearNet(nn.Module):
    def __init__(self, num_inputs, num_outputs):
        super(LinearNet, self).__init__()
        self.linear = nn.Linear(num_inputs, num_outputs)
    def forward(self, x): # x shape: (batch, 1, 28, 28)
        y = self.linear(x.view(x.shape[0], -1))
        return y
net = LinearNet(num_inputs, num_outputs)

         我们将x形状转化的功能函数自定义一个FlattenLayer并记录在d2llzh_pytorch中方便后面使用。

class FlattenLayer(nn.Module):
    def __init__(self):
        super(FlattenLayer, self).__init__()
    def forward(self, x): # x shape: (batch, *, *, ...)
        return x.view(x.shape[0], -1)

        这样我们就可以更更⽅方便便地定义我们的模型:

from collections import OrderedDict
net = nn.Sequential(
    # FlattenLayer(),
    # nn.Linear(num_inputs, num_outputs)
    OrderedDict([
        ('flatten', FlattenLayer()),
        ('linear', nn.Linear(num_inputs, num_outputs))])
    )

        然后,我们使⽤用均值为0、标准差为0.01的正态分布随机初始化模型的权重参数。

init.normal_(net.linear.weight, mean=0, std=0.01)
init.constant_(net.linear.bias, val=0)

        PyTorch提供了了一个包括softmax运算和交叉熵损失计算的函数。它的数值稳定性更好。

loss = nn.CrossEntropyLoss()

        我们使⽤用学习率为0.1的小批量量随机梯度下降作为优化算法。

optimizer = torch.optim.SGD(net.parameters(), lr=0.1)

        接下来,我们使用训练函数来训练模型。

def train_ch3(net, train_iter, test_iter, loss, num_epochs,batch_size,params=None, lr=None,optimizer=None):
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y).sum()
            # 梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()
            l.backward()
            if optimizer is None:
                d2l.sgd(params, lr, batch_size)
            else:
                optimizer.step() # “softmax回归的简洁实现”⼀一节将⽤用到
            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) ==y).sum().item()
            n += y.shape[0]
            test_acc = evaluate_accuracy(test_iter, net)
            print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'% (epoch + 1,                                         
train_l_sum / n, train_acc_sum / n,test_acc))
num_epochs = 5
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs,
batch_size, None, None, optimizer)

输出:

poch 1, loss 0.0031, train acc 0.745, test acc 0.790
epoch 2, loss 0.0022, train acc 0.812, test acc 0.807
epoch 3, loss 0.0021, train acc 0.825, test acc 0.806
epoch 4, loss 0.0020, train acc 0.832, test acc 0.810
epoch 5, loss 0.0019, train acc 0.838, test acc 0.823

参考: Dive-into-DL-PyTorch

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 首先,我们需要导入必要的包和数据集: ```python import numpy as np from sklearn.datasets import load_iris iris = load_iris() X = iris.data y = iris.target ``` 然后,我们需要将标签 `y` 转换为 one-hot 编码格式: ```python n_classes = 3 y_one_hot = np.zeros((len(y), n_classes)) y_one_hot[np.arange(len(y)), y] = 1 ``` 接下来,我们需要划分数据集为训练集和测试集: ```python from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y_one_hot, test_size=0.2, random_state=42) ``` 然后,我们需要定义一些辅助函数: ```python def sigmoid(z): return 1 / (1 + np.exp(-z)) def softmax(z): exp_scores = np.exp(z) return exp_scores / np.sum(exp_scores, axis=1, keepdims=True) def loss_function(y_pred, y_true): return -np.mean(y_true * np.log(y_pred)) def predict(X, W): return softmax(np.dot(X, W)) def accuracy(y_pred, y_true): return np.mean(np.argmax(y_pred, axis=1) == np.argmax(y_true, axis=1)) ``` 接着,我们需要初始化模型参数: ```python n_features = X.shape[1] n_classes = 3 W = np.random.randn(n_features, n_classes) ``` 然后,我们可以开始训练模型: ```python learning_rate = 0.1 n_epochs = 1000 for epoch in range(n_epochs): # Forward propagation y_pred = predict(X_train, W) # Backward propagation dW = np.dot(X_train.T, (y_pred - y_train)) / len(X_train) # Update parameters W -= learning_rate * dW # Print loss and accuracy every 100 epochs if epoch % 100 == 0: loss = loss_function(y_pred, y_train) acc = accuracy(y_pred, y_train) print(f"Epoch {epoch}: Loss = {loss}, Accuracy = {acc}") ``` 最后,我们可以使用测试集来评估模型的性能: ```python y_pred_test = predict(X_test, W) test_loss = loss_function(y_pred_test, y_test) test_acc = accuracy(y_pred_test, y_test) print(f"Test Loss = {test_loss}, Test Accuracy = {test_acc}") ``` 完整代码如下: ```python import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split def sigmoid(z): return 1 / (1 + np.exp(-z)) def softmax(z): exp_scores = np.exp(z) return exp_scores / np.sum(exp_scores, axis=1, keepdims=True) def loss_function(y_pred, y_true): return -np.mean(y_true * np.log(y_pred)) def predict(X, W): return softmax(np.dot(X, W)) def accuracy(y_pred, y_true): return np.mean(np.argmax(y_pred, axis=1) == np.argmax(y_true, axis=1)) iris = load_iris() X = iris.data y = iris.target n_classes = 3 y_one_hot = np.zeros((len(y), n_classes)) y_one_hot[np.arange(len(y)), y] = 1 X_train, X_test, y_train, y_test = train_test_split(X, y_one_hot, test_size=0.2, random_state=42) n_features = X.shape[1] n_classes = 3 W = np.random.randn(n_features, n_classes) learning_rate = 0.1 n_epochs = 1000 for epoch in range(n_epochs): # Forward propagation y_pred = predict(X_train, W) # Backward propagation dW = np.dot(X_train.T, (y_pred - y_train)) / len(X_train) # Update parameters W -= learning_rate * dW # Print loss and accuracy every 100 epochs if epoch % 100 == 0: loss = loss_function(y_pred, y_train) acc = accuracy(y_pred, y_train) print(f"Epoch {epoch}: Loss = {loss}, Accuracy = {acc}") y_pred_test = predict(X_test, W) test_loss = loss_function(y_pred_test, y_test) test_acc = accuracy(y_pred_test, y_test) print(f"Test Loss = {test_loss}, Test Accuracy = {test_acc}") ``` ### 回答2: numpy 是一个用于科学计算的强大库,它提供了许多高级数学函数和工具。我们可以使用 numpy 来编写逻辑回归算法对 iris 数据进行多分类。 首先,我们需要导入所需的库和数据集。我们可以使用 sklearn 库中的 `load_iris` 函数来加载 iris 数据集。然后,我们将数据集划分为特征矩阵 `X` 和目标向量 `y`。 ``` import numpy as np import sklearn.datasets # 加载 iris 数据集 iris = sklearn.datasets.load_iris() X = iris.data y = iris.target ``` 接下来,我们需要对目标向量 `y` 进行独热编码。独热编码将目标向量中的每个类别转换为一个二进制向量,其中只有一个元素为 1,表示该样本属于该类别,在其他位置上的元素都为 0。 ``` # 对目标向量进行独热编码 n_classes = len(np.unique(y)) y_encoded = np.zeros((len(y), n_classes)) y_encoded[np.arange(len(y)), y] = 1 ``` 然后,我们需要定义逻辑回归模型的参数,包括权重矩阵 `W` 和偏差矩阵 `b`。 ``` # 定义模型参数 n_features = X.shape[1] n_samples = X.shape[0] W = np.zeros((n_features, n_classes)) b = np.zeros((1, n_classes)) ``` 接下来,我们定义 Sigmoid 函数,它将任何实数映射到范围 (0, 1) 内。这个函数将用于计算模型的输出。 ``` # 定义 Sigmoid 函数 def sigmoid(z): return 1 / (1 + np.exp(-z)) ``` 然后,我们可以实现逻辑回归模型的前向传播和反向传播算法。 ``` # 定义前向传播和反向传播算法 def forward_propagation(X, W, b): Z = np.dot(X, W) + b A = sigmoid(Z) return A def backward_propagation(X, A, Y): dZ = A - Y dW = np.dot(X.T, dZ) / n_samples db = np.sum(dZ, axis=0, keepdims=True) / n_samples return dW, db ``` 最后,我们可以使用梯度下降算法来更新模型的参数。 ``` # 定义梯度下降算法 def gradient_descent(X, Y, W, b, learning_rate, num_iterations): for i in range(num_iterations): A = forward_propagation(X, W, b) dW, db = backward_propagation(X, A, Y) W -= learning_rate * dW b -= learning_rate * db ``` 调用上述函数,我们可以使用逻辑回归模型来训练并预测 iris 数据集的多个类别。 ``` # 定义和训练模型 learning_rate = 0.01 num_iterations = 1000 gradient_descent(X, y_encoded, W, b, learning_rate, num_iterations) # 预测类别 predictions = forward_propagation(X, W, b) predicted_classes = np.argmax(predictions, axis=1) ``` 这样,我们可以使用 numpy 编写逻辑回归算法对 iris 数据进行多分类。 ### 回答3: NumPy是一个功能强大的Python科学计算库,可以用来进行数值计算和数据处理。在使用NumPy编写逻辑回归算法对iris数据进行多分类时,需要先导入NumPy库,然后读取和处理iris数据,最后实现逻辑回归算法。 具体步骤如下: 1. 导入NumPy库和iris数据集。可以使用`import numpy as np`导入NumPy库,并使用`from sklearn import datasets`导入iris数据集。 2. 加载iris数据集并进行数据预处理。可以使用`datasets.load_iris()`加载iris数据集,然后将数据集分为特征和标签。使用`iris.data`获取特征数据,使用`iris.target`获取标签数据。 3. 对特征数据进行标准化处理。可以使用`np.mean`和`np.std`计算特征数据的平均值和标准差,然后对每个特征进行标准化处理。 4. 将标签数据进行独热编码。可以使用`np.eye`将标签数据转换为独热编码形式。 5. 定义逻辑回归模型及其参数。逻辑回归模型使用sigmoid函数作为激活函数,可以定义一个`sigmoid`函数来计算激活值。模型的参数包括权重`W`和偏差`b`,可以使用NumPy的随机函数生成初始参数值。 6. 定义损失函数和梯度下降算法。可以使用交叉熵损失函数计算损失,并使用梯度下降算法更新参数。 7. 使用训练数据训练模型。可以使用for循环迭代训练过程,在每个迭代步骤中计算损失和梯度,然后更新参数。 8. 使用测试数据评估模型性能。将测试数据输入模型中,得到预测结果,然后与实际结果进行比较,计算准确率或其他评价指标。 以上是基本的逻辑回归多分类算法的步骤,可以根据实际需求进行细节调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值