NNDL 实验三 线性回归

使用pytorch实现
2.2 线性回归
2.2.1 数据集构建
构造一个小的回归数据集:

生成 150 个带噪音的样本,其中 100 个训练样本,50 个测试样本,并打印出训练数据的可视化分布。

# 真实函数的参数缺省值为 w=1.2,b=0.5
def linear_func(x,w=1.2,b=0.5):
    y = w*x + b
    return y

import torch


def create_toy_data(func, interval, sample_num, noise = 0.0, add_outlier = False, outlier_ratio = 0.001):
    """
    根据给定的函数,生成样本
    输入:
       - func:函数
       - interval: x的取值范围
       - sample_num: 样本数目
       - noise: 噪声均方差
       - add_outlier:是否生成异常值
       - outlier_ratio:异常值占比
    输出:
       - X: 特征数据,shape=[n_samples,1]
       - y: 标签数据,shape=[n_samples,1]
    """

    # 均匀采样
    # 使用torch.rand在生成sample_num个随机数
    X = torch.rand(size = [sample_num]) * (interval[1]-interval[0]) + interval[0]
    y = func(X)
    # 生成高斯分布的标签噪声
    # 使用torch.normal生成0均值,noise标准差的数据
    epsilon = torch.normal(0,noise,y.shape)
    y = y + epsilon
    if add_outlier:     # 生成额外的异常点
        outlier_num = int(len(y)*outlier_ratio)
        if outlier_num != 0:
            # 使用paddle.randint生成服从均匀分布的、范围在[0, len(y))的随机Tensor
            outlier_idx = torch.randint(len(y),size = [outlier_num])
            y[outlier_idx] = y[outlier_idx] * 5
    return X, y

from matplotlib import pyplot as plt # matplotlib 是 Python 的绘图库

func = linear_func
interval = (-10,10)
train_num = 100 # 训练样本数目
test_num = 50 # 测试样本数目
noise = 2
X_train, y_train = create_toy_data(func=func, interval=interval, sample_num=train_num, noise = noise, add_outlier = False)
X_test, y_test = create_toy_data(func=func, interval=interval, sample_num=test_num, noise = noise, add_outlier = False)

X_train_large, y_train_large = create_toy_data(func=func, interval=interval, sample_num=5000, noise = noise, add_outlier = False)

# torch.linspace返回一个Tensor,Tensor的值为在区间start和stop上均匀间隔的num个值,输出Tensor的长度为num
X_underlying = torch.linspace(interval[0],interval[1],train_num)
y_underlying = linear_func(X_underlying)

# 绘制数据
plt.scatter(X_train, y_train, marker='*', facecolor="none", edgecolor='#e4007f', s=50, label="train data")
plt.scatter(X_test, y_test, facecolor="none", edgecolor='#f19ec2', s=50, label="test data")
plt.plot(X_underlying, y_underlying, c='#000000', label=r"underlying distribution")
plt.legend(fontsize='x-large') # 给图像加图例
plt.savefig('ml-vis.pdf') # 保存图像到PDF文件中
plt.show()

2.2.2 模型构建


import torch
from nndl.op import Op

torch.manual_seed(10)  # 设置随机种子


# 线性算子
class Linear(Op):
    def __init__(self, input_size):
        """
        输入:
           - input_size:模型要处理的数据特征向量长度
        """

        self.input_size = input_size

        # 模型参数
        self.params = {}
        self.params['w'] = torch.randn(size=[self.input_size, 1], dtype=torch.float32)
        self.params['b'] = torch.zeros(size=[1], dtype=torch.float32)

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

    # 前向函数
    def forward(self, X):
        """
        输入:
           - X: tensor, shape=[N,D]
           注意这里的X矩阵是由N个x向量的转置拼接成的,与原教材行向量表示方式不一致
        输出:
           - y_pred: tensor, shape=[N]
        """

        N, D = X.shape

        if self.input_size == 0:
            return torch.full(shape=[N, 1], fill_value=self.params['b'])

        assert D == self.input_size  # 输入数据维度合法性验证

        # 使用torch.matmul计算两个tensor的乘积
        y_pred = torch.matmul(X, self.params['w']) + self.params['b']

        return y_pred


# 注意这里我们为了和后面章节统一,这里的X矩阵是由N个x向量的转置拼接成的,与原教材行向量表示方式不一致
input_size = 3
N = 2
X = torch.randn(N, input_size)  # 生成2个维度为3的数据
model = Linear(input_size)
y_pred = model(X)
print("y_pred:", y_pred)  # 输出结果的个数也是2个


2.2.3 损失函数
回归任务中常用的评估指标是均方误差

均方误差(mean-square error, MSE)是反映估计量与被估计量之间差异程度的一种度量。

【注意:代码实现中没有除2】思考:没有除2合理么?谈谈自己的看法,写到实验报告。


#损失函数
def mean_squared_error(y_true, y_pred):
    """
    输入:
       - y_true: tensor,样本真实标签
       - y_pred: tensor, 样本预测标签
    输出:
       - error: float,误差值
    """

    assert y_true.shape[0] == y_pred.shape[0]

    # torch.square计算输入的平方值
    # torch.mean沿 axis 计算 x 的平均值,默认axis是None,则对输入的全部元素计算平均值。
    error = torch.mean(torch.square(y_true - y_pred))

    return error


# 构造一个简单的样例进行测试:[N,1], N=2
y_true = torch.tensor([[-0.2], [4.9]], dtype=torch.float32)
y_pred = torch.tensor([[1.3], [2.5]], dtype=torch.float32)

error = mean_squared_error(y_true=y_true, y_pred=y_pred).item()
print("error:", error)

答:合理,除2不影响相对的均方误差。

2.2.4 模型优化
经验风险 ( Empirical Risk ),即在训练集上的平均损失。

思考1. 为什么省略了不影响效果?

思考 2.  什么是最小二乘法 ( Least Square Method , LSM )
回答以上问题,写到实验报告。 



#模型优化
def optimizer_lsm(model, X, y, reg_lambda=0):
    """
      输入:
         - model: 模型
         - X: tensor, 特征数据,shape=[N,D]
         - y: tensor,标签数据,shape=[N]
         - reg_lambda: float, 正则化系数,默认为0
      输出:
         - model: 优化好的模型
      """

    N, D = X.shape

    # 对输入特征数据所有特征向量求平均
    x_bar_tran = torch.mean(X, axis=0).T

    # 求标签的均值,shape=[1]
    y_bar = torch.mean(y)

    # paddle.subtract通过广播的方式实现矩阵减向量
    x_sub =  torch.subtract(X, x_bar_tran)

    # 使用paddle.all判断输入tensor是否全0
    if  torch.all(x_sub == 0):
        model.params['b'] = y_bar
        model.params['w'] = torch.zeros(shape=[D])
        return model

    # paddle.inverse求方阵的逆
    tmp =  torch.inverse( torch.matmul(x_sub.T, x_sub) +
                         reg_lambda * torch.eye(num_rows=(D)))

    w =  torch.matmul(torch.matmul(tmp, x_sub.T), (y - y_bar))

    b = y_bar -  torch.matmul(x_bar_tran, w)

    model.params['b'] = b
    model.params['w'
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值