【动手学深度学习】线性回归从零实现

动手学深度学习

  • 线性回归从零实现
    • 生成数据集
        • tf.zeros()
        • tf.random.normal
        • tf.reshape()
        • tf.matmul()
    • 读取数据
        • range()
        • random.shuffle()
        • tf.constant
        • yield()
    • 初始化模型参数
    • 定义模型
    • 定义损失函数
    • 定义优化算法
    • 训练
        • tf.GradientTape()
    • 训练结果和误差

线性回归从零实现

生成数据集

生成数据集(带有噪声的线性模型) w=[2,-3.4]^T、b=4.2和噪声项σ生成数据集和标签(σ即模型预测和标签时的观测误差),任务是恢复模型的参数 ,尽量使用低维数据(容易可视化,看得清形状)


tf.zeros()

X=tf.zeros((num_examples,w.shape[0]))

tf.zeros()是一个创建一个所有元素都为0的张量,后面的参数是形状。此处num_examples=1000,w.shape[0]=2,所以创建了shape=(1000, 2), dtype=float32的张量。


tf.random.normal

X+=tf.random.normal(shape=X.shape)

tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)

tf.random_normal用于从正态分布中输出随机值

  • shape: 输出张量的形状,必选
  • mean: 正态分布的均值,默认为0
  • stddev: 正态分布的标准差,默认为1.0
  • dtype: 输出的类型,默认为tf.float32
  • seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样
  • name: 操作的名称

tf.reshape()

tf.reshape(
    tensor, shape, name=None
)
  • tensor Tensor张量
  • shape Tensor张量,用于定义输出张量的shape,组成元素类型为 int32或int64.
  • name 可选参数,用于定义操作名称.

返回值也是一tensor,输出类型和输入类型相同
tf.reshape函数用于对输入tensor进行维度调整,不会修改内部元素的数量以及元素之间的顺序,如果需要修改张量的维度来实现对元素重新排序,需要使用tf.transpose。(来源)

reshape(-1,1)中的-1是由整体形状确定这个参数,比如[1,6]变成[-1,3]
就是[2,3]


tf.matmul()

tf.matmul()矩阵相乘

该部分代码(生成数据集)

def synthetic_data(w,b,num_examples):
    X=tf.zeros((num_examples,w.shape[0]))
    X+=tf.random.normal(shape=X.shape)
    y=tf.matmul(X,tf.reshape(w,(-1,1)))+b
    y+=tf.random.normal(shape=y.shape,stddev=0.01)
    y=tf.reshape(y,(-1,1))

    return X,y

true_w=tf.constant([2,-3.4])
true_b=4.2
features,labels=synthetic_data(true_w,true_b,1000)

读取数据

读取数据,该函数接收批量大小、特征矩阵和标签向量,生成batch_size大小的小批量(包括一组特征和标签)

num_examples=len(features)  #数据数量就由特征矩阵长度得到

range()

range(start, stop, step)
range() 函数返回数字序列,默认从 0 开始,默认以 1 递增,并以指定的数字结束。

step为步长,每次挪动这么多,如3,6,9,12的step为3


random.shuffle()

random.shuffle()用于将一个列表中的元素打乱顺序


tf.constant

tf.constant(
    value,
    dtype=None,
    shape=None,
    name='Const',
    verify_shape=False
)

创建一个常量tensor,按照给出value来赋值,可以用shape来指定其形状。value可以是一个数,也可以是一个list。value是必须的。

for i in range(0,num_examples,batch_size):
	j=tf.constant(indices[i:min(i+batch_size,num_examples)])

这里是把打乱后的索引从0开始到num_examples以batch_size的大小生成数据

该部分代码(读取数据):

def data_iter(batch_size,features,labels):
    num_examples=len(features)  #数据数量就由特征矩阵长度得到
    #print(len(features))
    indices=list(range(num_examples))
    print(indices)
    # 随机读取样本
    random.shuffle(indices)
    for i in range(0,num_examples,batch_size):
        j=tf.constant(indices[i:min(i+batch_size,num_examples)])
        yield tf.gather(features,j),tf.gather(labels,j)

yield()

先简单把他理解成return,带yield的函数是一个生成器,而不是一个函数,使用next()就会从上次执行结束的地方继续执行。

yield()用法


初始化模型参数

通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。

初始化参数之后更新参数,直到这些参数足够拟合我们的数据。每次更新都需要计算损失函数关于模型参数的梯度,有了这个梯度就可以向减小损失的方向更新每个参数。

w = tf.Variable(tf.random.normal(shape=(2, 1), mean=0, stddev=0.01),
                trainable=True)
b = tf.Variable(tf.zeros(1), trainable=True)

定义模型

将模型的输入和参数同模型的输出关联起来。

def linreg(X,w,b):
    return tf.matmul(X,w)+b
    

定义损失函数

定义损失函数来计算损失函数的梯度。
这里使用平方损失函数,此处需要将真实值y的形状转换为和预测值y_hat的形状相同。

在上面的代码中我们可以看出y的形状是(1000,1)。

y=tf.reshape(y,(-1,1))

看一下预测值y_hat的形状,在下面训练部分代码中y_hat是linreg(X,w,b)的返回值,返回的就是tf.matmul(X,w)+b,X的形状是(1000,2),w的形状是(2,0)。

def squared_loss(y_hat,y):
    return (y_hat-tf.reshape(y,y_hat.shape))**2/2

定义优化算法

小批量随机梯度下降法

在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。 接下来,朝着减少损失的方向更新我们的参数。
该函数接受模型参数集合学习速率批量大小作为输入。每一步更新的大小由学习速率lr决定。
计算的损失是一个批量样本的总和,所以我们用批量大小(batch_size) 来规范化步长,这样步长大小就不会取决于我们对批量大小的选择。

def sgd(params, grads, lr, batch_size):
    for param,grad in zip(params,grads):
        param.assign_sub(lr*grad/batch_size)

训练

1.在每次迭代中,读取一小批量训练样本,通过模型来获得一组预测。
2.计算完损失后,开始反向传播,存储每个参数的梯度。
3.最后,调用优化算法sgd来更新模型参数。

在每个迭代周期(epoch)中,使用data_iter函数遍历整个数据集, 并将训练数据集中所有样本都使用一次(假设样本数能够被批量大小整除)。
迭代周期个数num_epochs和学习率lr都是超参数。


tf.GradientTape()

梯度求解利器


该部分代码(训练):

lr = 0.03  #学习率
num_epochs = 3 #轮数
net = linreg #网络类型:线性回归
loss = squared_loss #损失函数:均方损失

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        with tf.GradientTape() as g:
            l = loss(net(X, w, b), y)  # X和y的小批量损失
        # 计算l关于[w,b]的梯度
        dw, db = g.gradient(l, [w, b])
        # 使用参数的梯度更新参数
        sgd([w, b], [dw, db], lr, batch_size)
    train_l = loss(net(features, w, b), labels)
    print(f'epoch {epoch + 1}, loss {float(tf.reduce_mean(train_l)):f}')

训练结果和误差

epoch 1, loss 0.000059
epoch 2, loss 0.000053
epoch 3, loss 0.000053

w的估计误差: [-7.3075294e-04 2.5033951e-05]
b的估计误差: [0.00141048]

线性模型的基本实现已经大概了解了,但是其中变量类型、参数传递和函数用法还不是很熟悉,需要再多看多敲多练,笔记中的文字有很多参考,摘抄于自动手深度学习网站的3.2节。

你可能感兴趣的:(深度学习,线性回归,python)