深度学习实验3

0 那些手动paddle转torch的代码段

paddel->torch
Layer->Module
Conv2D->Conv2d
axis->dim
删除权重初始化变量ParamAttr(...)将其变量改到初始化形参数据之前
torch没有相应的torch.io
paddle.seed()->torch.random.manual_seed()#设置随机种子
paddle.metric->使用torchmetrics手动重写(Torch生态的库,需要另装)#评价指标模块
paddle.cast(tensor,dtype)->tensor.to(dtype=torch.dtype)#类型转换
paddle.equal->使用==(torch.equal返回整个Tensor是否相等的布尔值,==返回一个各个元素是否相等的布尔张量)
paddle.to_tensor()->torch.to() or torch.tensor()
tensor.astype('dtype')->.to(torch.dtype)
torch.nn.functional.one_hot(tensor,size)第一个tensor参数的数据类型为LongTensor,也就是torch.int64类型的
paddle.mean(torch.cast(paddle.equal(preds, labels),dtype='float32'))  ->torch.mean(torch.as_tensor((preds==labels),dtype=torch.float32))

完成对应的替换基本90%的代码段就能跑通了,剩下的报错手动改
将上面的对应关系改完,那邱锡鹏老师实现的nndl库基本就能用torch运行了。
其实在网络上有更多的paddle和Torch对应的API的对应表,我这里就贴一个简单的:

第3章 线性分类

3.1 基于Logistic回归的二分类任务

3.1.1 数据集构建

def make_moons(n_samples=1000, shuffle=True, noise=None):
    """
    生成带噪音的弯月形状数据
    输入:
        - n_samples:数据量大小,数据类型为int
        - shuffle:是否打乱数据,数据类型为bool
        - noise:以多大的程度增加噪声,数据类型为None或float,noise为None时表示不增加噪声
    输出:
        - X:特征数据,size=[n_samples,2]
        - y:标签数据, size=[n_samples]
    """
    n_samples_out = n_samples // 2
    n_samples_in = n_samples - n_samples_out

    #采集第1类数据,特征为(x,y)
    #使用'torch.linspace'在0到pi上均匀取n_samples_out个值
    #使用'torch.cos'计算上述取值的余弦值作为特征1,使用'torch.sin'计算上述取值的正弦值作为特征2
    outer_circ_x = torch.cos(torch.linspace(0, math.pi, n_samples_out))
    outer_circ_y = torch.sin(torch.linspace(0, math.pi, n_samples_out))

    inner_circ_x = 1 - torch.cos(torch.linspace(0, math.pi, n_samples_in))
    inner_circ_y = 0.5 - torch.sin(torch.linspace(0, math.pi, n_samples_in))
    
    print('outer_circ_x.shape:', outer_circ_x.shape, 'outer_circ_y.shape:', outer_circ_y.shape)
    print('inner_circ_x.shape:', inner_circ_x.shape, 'inner_circ_y.shape:', inner_circ_y.shape)
    
    #使用'torch.cat'将两类数据的特征1和特征2分别延维度0拼接在一起,得到全部特征1和特征2
    #使用'torch.stack'将两类特征延维度1堆叠在一起
    X = torch.stack(
        [torch.cat([outer_circ_x, inner_circ_x]),
        torch.cat([outer_circ_y, inner_circ_y])],
        dim=1
    )

    print('after cat shape:', torch.cat([outer_circ_x, inner_circ_x]).shape)
    print('X shape:', X.shape)

    #使用'torch. zeros'将第一类数据的标签全部设置为0
    #使用'torch. ones'将第一类数据的标签全部设置为1
    y = torch.cat(
        [torch.zeros(size=[n_samples_out]), torch.ones(size=[n_samples_in])]
    )

    print('y shape:', y.shape)

    #如果shuffle为True,将所有数据打乱
    if shuffle:
        #使用'torch.randperm'生成一个数值在0到X.shape[0],随机排列的一维Tensor做索引值,用于打乱数据
        idx = torch.randperm(X.shape[0])
        X = X[idx]
        y = y[idx]

    #如果noise不为None,则给特征值加入噪声
    if noise is not None:
        #使用'torch.normal'生成符合正态分布的随机Tensor作为噪声,并加到原始特征上
        X += torch.normal(mean=0.0, std=noise, size=X.shape)

    return X, y

paddle与pytorch不兼容问题 paddle转pytorch_ci


划分数据集:将1000个样本拆分成训练集、验证集和测试集,其中训练集640条,验证集160条、测试集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])

3.1.2 模型构建

def logistic(x):
    return 1 / (1 + torch.exp(-x))

函数图像如图:

paddle与pytorch不兼容问题 paddle转pytorch_ci_02


我们构建一个Logistic回归算子,代码实现如下:

lass model_LR(Op):
    def __init__(self, input_dim):
        super(model_LR, self).__init__()
        # 存放线性层参数
        self.params = {}
        # 将线性层的权重参数全部初始化为0
        self.params['w'] = torch.zeros(size=[input_dim, 1])
        # self.params['w'] = torch.normal(mean=0, std=0.01, size=[input_dim, 1])
        # 将线性层的偏置参数初始化为0
        self.params['b'] = torch.zeros(size=[1])
        # 存放参数的梯度
        self.grads = {}
        self.X = None
        self.outputs = None

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

    def forward(self, inputs):
        self.X = inputs
        # 线性计算
        score = torch.matmul(inputs, self.params['w']) + self.params['b']
        # Logistic 函数
        self.outputs = logistic(score)
        return self.outputs

    def backward(self, labels):
        """
        输入:
            - labels:真实标签,size=[N, 1]
        """
        N = labels.shape[0]
        # 计算偏导数
        self.grads['w'] = -1 / N * torch.matmul(self.X.t(), (labels - self.outputs))
        self.grads['b'] = -1 / N * torch.sum(labels - self.outputs)

问题1:在各个书中logistic回归有几率回归,逻辑斯特回归,逻辑斯迪克回归等,没有对应的中文的最好就用英文原文。
问题2:
记过函数就是负责将神经元的数去映射到输出,神经网络中的几号函数主要是提供阶梯的非线性,常见的有Sigmod函数,tanh函数,relu函数等

3.1.3损失函数

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

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

    def forward(self, predicts, labels):
        """
        输入:
            - predicts:预测值,size=[N, 1],N为样本数量
            - labels:真实标签,size=[N, 1]
        输出:
            - 损失值:size=[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, dim=1)
        return loss
    def backward(self,labels):
        pass

3.1.4模型优化

SGD优化器:

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

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

#新增梯度下降法优化器
class SimpleBatchGD(Optimizer):
    def __init__(self, init_lr, model):
        super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)

    def step(self):
        #参数更新
        #遍历所有参数,按照公式(3.8)和(3.9)更新参数
        if isinstance(self.model.params, dict):
            for key in self.model.params.keys():
                self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]

3.1.5评价指标

准确率评价指标:

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

运行结果:

outer_circ_x.shape: torch.Size([500]) outer_circ_y.shape: torch.Size([500])
inner_circ_x.shape: torch.Size([500]) inner_circ_y.shape: torch.Size([500])
after cat shape: torch.Size([1000])
X shape: torch.Size([1000, 2])
y shape: torch.Size([1000])
X_train shape:  torch.Size([640, 2]) y_train shape:  torch.Size([640, 1])
tensor([[1.],
        [1.],
        [1.],
        [0.],
        [0.]])
best accuracy performence has been updated: 0.00000 --> 0.75000
[Train] epoch: 0, loss: 0.693146288394928, score: 0.5
[Dev] epoch: 0, loss: 0.6844645738601685, score: 0.75
[Train] epoch: 50, loss: 0.48319950699806213, score: 0.807812511920929
[Dev] epoch: 50, loss: 0.519908607006073, score: 0.75
[Train] epoch: 100, loss: 0.4398562014102936, score: 0.8140624761581421
[Dev] epoch: 100, loss: 0.4893949627876282, score: 0.75
best accuracy performence has been updated: 0.75000 --> 0.75625
[Train] epoch: 150, loss: 0.42317506670951843, score: 0.817187488079071
[Dev] epoch: 150, loss: 0.479976624250412, score: 0.7562500238418579
best accuracy performence has been updated: 0.75625 --> 0.76250
[Train] epoch: 200, loss: 0.4150051176548004, score: 0.823437511920929
[Dev] epoch: 200, loss: 0.47652289271354675, score: 0.762499988079071
[Train] epoch: 250, loss: 0.4104517996311188, score: 0.8203125
[Dev] epoch: 250, loss: 0.47522956132888794, score: 0.7437499761581421
[Train] epoch: 300, loss: 0.407705694437027, score: 0.8218749761581421
[Dev] epoch: 300, loss: 0.4748341143131256, score: 0.75
[Train] epoch: 350, loss: 0.40596142411231995, score: 0.823437511920929
[Dev] epoch: 350, loss: 0.4748414158821106, score: 0.7562500238418579
[Train] epoch: 400, loss: 0.40481358766555786, score: 0.8265625238418579
[Dev] epoch: 400, loss: 0.47503310441970825, score: 0.75
[Train] epoch: 450, loss: 0.40403881669044495, score: 0.828125
[Dev] epoch: 450, loss: 0.4753051698207855, score: 0.75
[Test] score/loss: 0.8100/0.4706

完善Runner类

#新增RunnerV2类
class RunnerV2(object):
    def __init__(self, model, optimizer, metric, loss_fn):
        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)
        #传入模型保存路径,如果没有传入值则默认为"best_model.pdparams"
        save_path = kwargs.get("save_path", "best_model.pdparams")
        #梯度打印函数,如果没有传入则默认为"None"
        print_grads = kwargs.get("print_grads", 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).item()
            self.train_loss.append(trn_loss)
            #计算评价指标
            trn_score = self.metric(logits, y).item()
            self.train_scores.append(trn_score)
            #计算参数梯度
            self.model.backward(y)
            if print_grads is not None:
                #打印每一层的梯度
                print_grads(self.model)
            #更新模型参数
            self.optimizer.step()
            dev_score, dev_loss = self.evaluate(dev_set)
            #如果当前指标为最优指标,保存该模型
            if dev_score > best_score:
                self.save_model(save_path)
                print(f"best accuracy performence has been updated: {best_score:.5f} --> {dev_score:.5f}")
                best_score = dev_score
            if epoch % log_epochs == 0:
                print(f"[Train] epoch: {epoch}, loss: {trn_loss}, score: {trn_score}")
                print(f"[Dev] epoch: {epoch}, loss: {dev_loss}, score: {dev_score}")
                
    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_path):
        torch.save(self.model.params, save_path)

    def load_model(self, model_path):
        self.model.params = torch.load(model_path)

3.1.7模型训练

# 固定随机种子,保持每次运行结果一致
torch.random.manual_seed(102)

# 特征维度
input_dim = 2
# 学习率
lr = 0.1

# 实例化模型
model = model_LR(input_dim=input_dim)
# 指定优化器
optimizer = SimpleBatchGD(init_lr=lr, model=model)
# 指定损失函数
loss_fn = BinaryCrossEntropyLoss()
# 指定评价方式
metric = accuracy

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

runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=500, log_epochs=50, save_path="best_model.pdparams")
plot(runner,fig_name='linear-acc.pdf')
score, loss = runner.evaluate([X_test, y_test])
print("[Test] score/loss: {:.4f}/{:.4f}".format(score, loss))
def decision_boundary(w, b, x1):
    w1, w2 = w
    x2 = (- w1 * x1 - b) / w2
    return x2
plt.figure(figsize=(5,5))
# 绘制原始数据
plt.scatter(X[:, 0].tolist(), X[:, 1].tolist(), marker='*', c=y.tolist())
w = model.params['w']
b = model.params['b']
x1 = torch.linspace(-2, 3, 1000)
x2 = decision_boundary(w, b, x1)
# 绘制决策边界
plt.plot(x1.tolist(), x2.tolist(), color="red")
plt.show()

可视化观察训练集与验证集的准确率和损失的变化情况。

# 可视化观察训练集与验证集的指标变化情况
def plot(runner,fig_name):
    plt.figure(figsize=(10,5))
    plt.subplot(1,2,1)
    epochs = [i for i in range(len(runner.train_scores))]
    # 绘制训练损失变化曲线
    plt.plot(epochs, runner.train_loss, color='#e4007f', label="Train loss")
    # 绘制评价损失变化曲线
    plt.plot(epochs, runner.dev_loss, color='#f19ec2', linestyle='--', label="Dev loss")
    # 绘制坐标轴和图例
    plt.ylabel("loss", fontsize='large')
    plt.xlabel("epoch", fontsize='large')
    plt.legend(loc='upper right', fontsize='x-large')
    plt.subplot(1,2,2)
    # 绘制训练准确率变化曲线
    plt.plot(epochs, runner.train_scores, color='#e4007f', label="Train accuracy")
    # 绘制评价准确率变化曲线
    plt.plot(epochs, runner.dev_scores, color='#f19ec2', linestyle='--', label="Dev accuracy")
    # 绘制坐标轴和图例
    plt.ylabel("score", fontsize='large')
    plt.xlabel("epoch", fontsize='large')
    plt.legend(loc='lower right', fontsize='x-large')
    plt.tight_layout()
    plt.savefig(fig_name)
    plt.show()
 
plot(runner,fig_name='linear-acc.pdf')

paddle与pytorch不兼容问题 paddle转pytorch_数据_03

3.1.8模型评价

score, loss = runner.evaluate([X_test, y_test])
print("[Test] score/loss: {:.4f}/{:.4f}".format(score, loss))
[Test] score/loss: 0.8100/0.4706

可视化边界:

paddle与pytorch不兼容问题 paddle转pytorch_ci_04

3.2 基于Softmax回归的多分类任务

3.2.1 数据集构建

#数据集构建
import numpy as np
import torch
import matplotlib.pyplot as plt
 
def make_multiclass_classification(n_samples=100, n_features=2, n_classes=3, shuffle=True, noise=0.1):
    """
    生成带噪音的多类别数据
    输入:
        - n_samples:数据量大小,数据类型为int
        - n_features:特征数量,数据类型为int
        - shuffle:是否打乱数据,数据类型为bool
        - noise:以多大的程度增加噪声,数据类型为None或float,noise为None时表示不增加噪声
    输出:
        - X:特征数据,shape=[n_samples,2]
        - y:标签数据, shape=[n_samples,1]
    """
    # 计算每个类别的样本数量
    n_samples_per_class = [int(n_samples / n_classes) for k in range(n_classes)]
    for i in range(n_samples - sum(n_samples_per_class)):
        n_samples_per_class[i % n_classes] += 1
    # 将特征和标签初始化为0
    X = torch.zeros([n_samples, n_features])
    y = torch.zeros([n_samples], dtype=torch.int32)
    # 随机生成3个簇中心作为类别中心
    centroids = torch.randperm(2 ** n_features)[:n_classes]
    centroids_bin = np.unpackbits(centroids.numpy().astype('uint8')).reshape((-1, 8))[:, -n_features:]
    centroids = torch.tensor(centroids_bin, dtype=torch.float32)
    # 控制簇中心的分离程度
    centroids = 1.5 * centroids - 1
    # 随机生成特征值
    X[:, :n_features] = torch.randn(size=[n_samples, n_features])
 
    stop = 0
    # 将每个类的特征值控制在簇中心附近
    for k, centroid in enumerate(centroids):
        start, stop = stop, stop + n_samples_per_class[k]
        # 指定标签值
        y[start:stop] = k % n_classes
        X_k = X[start:stop, :n_features]
        # 控制每个类别特征值的分散程度
        A = 2 * torch.rand(size=[n_features, n_features]) - 1
        X_k[...] = torch.matmul(X_k, A)
        X_k += centroid
        X[start:stop, :n_features] = X_k
 
    # 如果noise不为None,则给特征加入噪声
    if noise > 0.0:
        # 生成noise掩膜,用来指定给那些样本加入噪声
        noise_mask = torch.rand([n_samples]) < noise
        for i in range(len(noise_mask)):
            if noise_mask[i]:
                # 给加噪声的样本随机赋标签值
                y[i] = torch.randint(n_classes, size=[1],dtype=torch.int32)
    # 如果shuffle为True,将所有数据打乱
    if shuffle:
        idx = torch.randperm(X.shape[0])
        X = X[idx]
        y = y[idx]
 
    return X, y

创建结果:

paddle与pytorch不兼容问题 paddle转pytorch_paddle_05


将实验数据拆分成训练集、验证集和测试集。其中训练集640条、验证集160条、测试集200条。

结果:

3.2.2 模型构建

3.2.2.1softmax函数
#softmax算子
from nndl import op
class model_SR(op.Op):
    def __init__(self, input_dim, output_dim):
        super(model_SR, self).__init__()
        self.params = {}
        # 将线性层的权重参数全部初始化为0
        self.params['W'] = torch.zeros(size=[input_dim, output_dim])
        # self.params['W'] = torch.normal(mean=0, std=0.01, shape=[input_dim, output_dim])
        # 将线性层的偏置参数初始化为0
        self.params['b'] = torch.zeros(size=[output_dim])
        self.outputs = None
 
    def __call__(self, inputs):
        return self.forward(inputs)
 
    def forward(self, inputs):
        """
        输入:
            - inputs: shape=[N,D], N是样本数量,D是特征维度
        输出:
            - outputs:预测值,shape=[N,C],C是类别数
        """
        # 线性计算
        score = torch.matmul(inputs, self.params['W']) + self.params['b']
        # Softmax 函数
        self.outputs = softmax(score)
        return self.outputs
 
# 随机生成1条长度为4的数据
inputs = torch.randn(size=[1,4])
print('Input is:', inputs)
# 实例化模型,这里令输入长度为4,输出类别数为3
model = model_SR(input_dim=4, output_dim=3)
outputs = model(inputs)
print('Output is:', outputs)
3.2.2softmax回归算子
#softmax函数
# x为tensor
def softmax(X):
    """
    输入:
        - X:shape=[N, C],N为向量数量,C为向量维度
    """
    x_max = torch.max(X, axis=1, keepdim=True)#N,1
    x_exp = torch.exp(X - x_max.values)
    partition = torch.sum(x_exp, axis=1, keepdim=True)#N,1
    return x_exp / partition
 
# 观察softmax的计算方式
X = torch.as_tensor([[0.1, 0.2, 0.3, 0.4],[1,2,3,4]])
predict = softmax(X)
print(predict)

3.2.3损失函数

多类交叉熵损失函数

#softmax算子
from nndl import op
class model_SR(op.Op):
    def __init__(self, input_dim, output_dim):
        super(model_SR, self).__init__()
        self.params = {}
        # 将线性层的权重参数全部初始化为0
        self.params['W'] = torch.zeros(size=[input_dim, output_dim])
        # self.params['W'] = torch.normal(mean=0, std=0.01, shape=[input_dim, output_dim])
        # 将线性层的偏置参数初始化为0
        self.params['b'] = torch.zeros(size=[output_dim])
        self.outputs = None
 
    def __call__(self, inputs):
        return self.forward(inputs)
 
    def forward(self, inputs):
        """
        输入:
            - inputs: shape=[N,D], N是样本数量,D是特征维度
        输出:
            - outputs:预测值,shape=[N,C],C是类别数
        """
        # 线性计算
        score = torch.matmul(inputs, self.params['W']) + self.params['b']
        # Softmax 函数
        self.outputs = softmax(score)
        return self.outputs
 
# 随机生成1条长度为4的数据
inputs = torch.randn(size=[1,4])
print('Input is:', inputs)
# 实例化模型,这里令输入长度为4,输出类别数为3
model = model_SR(input_dim=4, output_dim=3)
outputs = model(inputs)
print('Output is:', outputs)

3.2.4模型优化

3.2.4.1梯度计算
#backard函数
class model_SR(op.Op):
    def __init__(self, input_dim, output_dim):
        super(model_SR, self).__init__()
        self.params = {}
        # 将线性层的权重参数全部初始化为0
        self.params['W'] = torch.zeros(size=[input_dim, output_dim])
        # self.params['W'] = torch.normal(mean=0, std=0.01, shape=[input_dim, output_dim])
        # 将线性层的偏置参数初始化为0
        self.params['b'] = torch.zeros(size=[output_dim])
        # 存放参数的梯度
        self.grads = {}
        self.X = None
        self.outputs = None
        self.output_dim = output_dim
 
    def __call__(self, inputs):
        return self.forward(inputs)
 
    def forward(self, inputs):
        self.X = inputs
        # 线性计算
        score = torch.matmul(self.X, self.params['W']) + self.params['b']
        # Softmax 函数
        self.outputs = softmax(score)
        return self.outputs
 
    def backward(self, labels):
        """
        输入:
            - labels:真实标签,shape=[N, 1],其中N为样本数量
        """
        # 计算偏导数
        N =labels.shape[0]
        labels = torch.nn.functional.one_hot(labels, self.output_dim)
        self.grads['W'] = -1 / N * torch.matmul(self.X.t(), (labels-self.outputs))
        self.grads['b'] = -1 / N * torch.matmul(torch.ones(size=[N]), (labels-self.outputs))

3.2.5.2模型训练

#模型训练
# 固定随机种子,保持每次运行结果一致
torch.random.manual_seed(102)
 
# 特征维度
input_dim = 2
# 类别数
output_dim = 3
# 学习率
lr = 0.1
 
# 实例化模型
model = model_SR(input_dim=input_dim, output_dim=output_dim)
# 指定优化器
optimizer = SimpleBatchGD(init_lr=lr, model=model)
# 指定损失函数
loss_fn = MultiCrossEntropyLoss()
# 指定评价方式
metric = accuracy
# 实例化RunnerV2类
runner = RunnerV2(model, optimizer, metric, loss_fn)
 
# 模型训练
runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=500, log_eopchs=50, eval_epochs=1, save_path="best_model.pdparams")
 
# 可视化观察训练集与验证集的准确率变化情况
plot(runner,fig_name='linear-acc2.pdf')

paddle与pytorch不兼容问题 paddle转pytorch_ci_06


paddle与pytorch不兼容问题 paddle转pytorch_paddle与pytorch不兼容问题_07

3.2.6模型评价

可视化观察结果划分结果

score, loss = runner.evaluate([X_test, y_test])
print("[Test] score/loss: {:.4f}/{:.4f}".format(score, loss))
[Test] score/loss: 0.8400/0.7016

3.3 实践:基于Softmax回归完成鸢尾花分类任务

3.3.1.2 数据清洗
#可视化分类结果
# 均匀生成40000个数据点
x1, x2 = torch.meshgrid(torch.linspace(-3.5, 2, 200), torch.linspace(-4.5, 3.5, 200))
x = torch.stack([torch.flatten(x1), torch.flatten(x2)], dim=1)
# 预测对应类别
y = runner.predict(x)
y = torch.argmax(y, dim=1)
# 绘制类别区域
plt.ylabel('x2')
plt.xlabel('x1')
plt.scatter(x[:,0].tolist(), x[:,1].tolist(), c=y.tolist(), cmap=plt.cm.Spectral)
 
torch.random.manual_seed(102)
n_samples = 1000
X, y = make_multiclass_classification(n_samples=n_samples, n_features=2, n_classes=3, noise=0.2)
 
plt.scatter(X[:, 0].tolist(), X[:, 1].tolist(), marker='*', c=y.tolist())
plt.show()

结果:

0
0

异常值处理:
不需要处理

3.3.1.3数据读取
import matplotlib.pyplot as plt #可视化工具
 
# 箱线图查看异常值分布
def boxplot(features):
    feature_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
 
    # 连续画几个图片
    plt.figure(figsize=(5, 5), dpi=200)
    # 子图调整
    plt.subplots_adjust(wspace=0.6)
    # 每个特征画一个箱线图
    for i in range(4):
        plt.subplot(2, 2, i+1)
        # 画箱线图
        plt.boxplot(features[:, i],
                    showmeans=True,
                    whiskerprops={"color":"#E20079", "linewidth":0.4, 'linestyle':"--"},
                    flierprops={"markersize":0.4},
                    meanprops={"markersize":1})
        # 图名
        plt.title(feature_names[i], fontdict={"size":5}, pad=2)
        # y方向刻度
        plt.yticks(fontsize=4, rotation=90)
        plt.tick_params(pad=0.5)
        # x方向刻度
        plt.xticks([])
    plt.savefig('ml-vis.pdf')
    plt.show()
 
boxplot(iris_features)

结果:

paddle与pytorch不兼容问题 paddle转pytorch_paddle_08

3.3.2模型构建:

from nndl import op
 
# 输入维度
input_dim = 4
# 类别数
output_dim = 3
# 实例化模型
model = op.model_SR(input_dim=input_dim, output_dim=output_dim)

3.3.3模型训练

from nndl import op, metric, opitimizer, RunnerV2
 
# 学习率
lr = 0.2
 
# 梯度下降法
optimizer = opitimizer.SimpleBatchGD(init_lr=lr, model=model)
# 交叉熵损失
loss_fn = op.MultiCrossEntropyLoss()
# 准确率
metric = metric.accuracy
 
# 实例化RunnerV2
runner = RunnerV2(model, optimizer, metric, loss_fn)
 
# 启动训练
runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=200, log_epochs=10, save_path="best_model.pdparams")

可视化训练集和验证集的准确率变化情况

from nndl import plot
plot(runner,fig_name='linear-acc3.pdf')

结果:

[Test] score/less: 0.7222/0.5999

模型评价

# 加载最优模型
runner.load_model('best_model.pdparams')
# 模型评价
score, loss = runner.evaluate([X_test, y_test])
print("[Test] score/loss: {:.4f}/{:.4f}".format(score, loss))
Lsl 17:53:49
X shape:  torch.Size([150, 4]) y shape:  torch.Size([150])
X_train shape:  torch.Size([120, 4]) y_train shape:  torch.Size([120])
tensor([1, 2, 0, 1, 2], dtype=torch.int32)
best accuracy performence has been updated: 0.00000 --> 0.46667
[Train] epoch: 0, loss: 1.09861159324646, score: 0.375
[Dev] epoch: 0, loss: 1.080685019493103, score: 0.46666666865348816
[Train] epoch: 10, loss: 0.8885007500648499, score: 0.699999988079071
[Dev] epoch: 10, loss: 0.9690309166908264, score: 0.46666666865348816
[Train] epoch: 20, loss: 0.7623857259750366, score: 0.699999988079071
[Dev] epoch: 20, loss: 0.8916819095611572, score: 0.46666666865348816
[Train] epoch: 30, loss: 0.6784879565238953, score: 0.7416666746139526
[Dev] epoch: 30, loss: 0.835434079170227, score: 0.46666666865348816
[Train] epoch: 40, loss: 0.6190740466117859, score: 0.7666666507720947
[Dev] epoch: 40, loss: 0.7935470342636108, score: 0.46666666865348816
[Train] epoch: 50, loss: 0.5746238231658936, score: 0.8166666626930237
[Dev] epoch: 50, loss: 0.7613646984100342, score: 0.46666666865348816
best accuracy performence has been updated: 0.46667 --> 0.53333
best accuracy performence has been updated: 0.53333 --> 0.60000
[Train] epoch: 60, loss: 0.5398635268211365, score: 0.824999988079071
[Dev] epoch: 60, loss: 0.7358842492103577, score: 0.6000000238418579
[Train] epoch: 70, loss: 0.5117151737213135, score: 0.8583333492279053
[Dev] epoch: 70, loss: 0.715168833732605, score: 0.6000000238418579
best accuracy performence has been updated: 0.60000 --> 0.66667
[Train] epoch: 80, loss: 0.48828545212745667, score: 0.875
[Dev] epoch: 80, loss: 0.6979429721832275, score: 0.6666666865348816
[Train] epoch: 90, loss: 0.4683511555194855, score: 0.875
[Dev] epoch: 90, loss: 0.6833425164222717, score: 0.6000000238418579
[Train] epoch: 100, loss: 0.45108893513679504, score: 0.8833333253860474
[Dev] epoch: 100, loss: 0.6707651019096375, score: 0.6000000238418579
[Train] epoch: 110, loss: 0.4359239935874939, score: 0.8916666507720947
[Dev] epoch: 110, loss: 0.6597797274589539, score: 0.6000000238418579
[Train] epoch: 120, loss: 0.42244184017181396, score: 0.8916666507720947
[Dev] epoch: 120, loss: 0.650070071220398, score: 0.6000000238418579
[Train] epoch: 130, loss: 0.4103356599807739, score: 0.8916666507720947
[Dev] epoch: 130, loss: 0.6413988471031189, score: 0.6000000238418579
[Train] epoch: 140, loss: 0.39937296509742737, score: 0.8999999761581421
[Dev] epoch: 140, loss: 0.6335848569869995, score: 0.6000000238418579
[Train] epoch: 150, loss: 0.38937342166900635, score: 0.9083333611488342
[Dev] epoch: 150, loss: 0.6264870762825012, score: 0.6000000238418579
[Train] epoch: 160, loss: 0.38019534945487976, score: 0.9166666865348816
[Dev] epoch: 160, loss: 0.6199939846992493, score: 0.6666666865348816
[Train] epoch: 170, loss: 0.3717249929904938, score: 0.9166666865348816
[Dev] epoch: 170, loss: 0.6140164136886597, score: 0.6666666865348816
[Train] epoch: 180, loss: 0.363870233297348, score: 0.925000011920929
[Dev] epoch: 180, loss: 0.6084824204444885, score: 0.6666666865348816
[Train] epoch: 190, loss: 0.3565550446510315, score: 0.925000011920929
[Dev] epoch: 190, loss: 0.60333251953125, score: 0.6666666865348816
[Train] epoch: 200, loss: 0.349716454744339, score: 0.925000011920929
[Dev] epoch: 200, loss: 0.5985181927680969, score: 0.6666666865348816
best accuracy performence has been updated: 0.66667 --> 0.73333
[Train] epoch: 210, loss: 0.3433011770248413, score: 0.9333333373069763
[Dev] epoch: 210, loss: 0.5939981937408447, score: 0.7333333492279053
[Train] epoch: 220, loss: 0.3372645676136017, score: 0.9333333373069763
[Dev] epoch: 220, loss: 0.5897385478019714, score: 0.7333333492279053
[Train] epoch: 230, loss: 0.3315680921077728, score: 0.9416666626930237
[Dev] epoch: 230, loss: 0.5857098698616028, score: 0.7333333492279053
[Train] epoch: 240, loss: 0.3261789083480835, score: 0.9416666626930237
[Dev] epoch: 240, loss: 0.5818875432014465, score: 0.7333333492279053
[Train] epoch: 250, loss: 0.32106834650039673, score: 0.9416666626930237
[Dev] epoch: 250, loss: 0.5782502889633179, score: 0.7333333492279053
[Train] epoch: 260, loss: 0.3162115514278412, score: 0.9416666626930237
[Dev] epoch: 260, loss: 0.5747794508934021, score: 0.7333333492279053
[Train] epoch: 270, loss: 0.31158649921417236, score: 0.949999988079071
[Dev] epoch: 270, loss: 0.5714595317840576, score: 0.7333333492279053
[Train] epoch: 280, loss: 0.30717405676841736, score: 0.9416666626930237
[Dev] epoch: 280, loss: 0.5682763457298279, score: 0.7333333492279053
[Train] epoch: 290, loss: 0.3029572665691376, score: 0.9416666626930237
[Dev] epoch: 290, loss: 0.5652178525924683, score: 0.7333333492279053
[Train] epoch: 300, loss: 0.29892098903656006, score: 0.949999988079071
[Dev] epoch: 300, loss: 0.5622733235359192, score: 0.7333333492279053
[Train] epoch: 310, loss: 0.29505160450935364, score: 0.949999988079071
[Dev] epoch: 310, loss: 0.5594334006309509, score: 0.7333333492279053
[Train] epoch: 320, loss: 0.2913372814655304, score: 0.949999988079071
[Dev] epoch: 320, loss: 0.5566895008087158, score: 0.7333333492279053
[Train] epoch: 330, loss: 0.2877667546272278, score: 0.949999988079071
[Dev] epoch: 330, loss: 0.5540345311164856, score: 0.7333333492279053
[Train] epoch: 340, loss: 0.28433048725128174, score: 0.949999988079071
[Dev] epoch: 340, loss: 0.5514616370201111, score: 0.7333333492279053
[Train] epoch: 350, loss: 0.28101956844329834, score: 0.949999988079071
[Dev] epoch: 350, loss: 0.5489650964736938, score: 0.7333333492279053
[Train] epoch: 360, loss: 0.2778260111808777, score: 0.949999988079071
[Dev] epoch: 360, loss: 0.5465391874313354, score: 0.7333333492279053
[Train] epoch: 370, loss: 0.2747423052787781, score: 0.949999988079071
[Dev] epoch: 370, loss: 0.5441796183586121, score: 0.7333333492279053
[Train] epoch: 380, loss: 0.27176186442375183, score: 0.949999988079071
[Dev] epoch: 380, loss: 0.5418818593025208, score: 0.7333333492279053
[Train] epoch: 390, loss: 0.2688787281513214, score: 0.949999988079071
[Dev] epoch: 390, loss: 0.5396421551704407, score: 0.7333333492279053
[Test] score/loss: 0.8667/0.4477

paddle与pytorch不兼容问题 paddle转pytorch_paddle_09

3.3.5模型预测

# 预测测试集数据
logits = runner.predict(X_test)
# 观察其中一条样本的预测结果
pred = torch.argmax(logits[0]).numpy()
# 获取该样本概率最大的类别
label = y_test[0].numpy()
# 输出真实类别与预测类别
print("The true category is {} and the predicted category is {}".format(label, pred))

结果:

The true category is 2 and the predicted category is 2

总结

主要是在改代码,意义不大,从头写需要很长时间,不改代码工作量比较大所以我总结了paddle->torch的方法

3.5实验拓展

习题1:尝试调整学习率和训练轮数等超参数,观察是否能够得到更高的精度;

将学习率调整为0.35,训练轮数调整为400

结果:

[Test] score/loss: 0.8668/0.4564