线性回归

监督学习

机器学习分为监督学习(supervied learning)无监督学习(unsupervised learning), 前者需要人为给定样本的分类标签作为先验, 后者完全根据算法的结果自动进行分类.

而监督学习又可以分为分类(classification)回归(regression), 前者针对离散变量而言, 后者针对连续变量而言.

回归和分类的区别在于, 回归问题的输出是一个连续的函数, 比如房价气温股票等的预测, 而分类问题的输出是一个离散的矩阵, 表示输入样本被分类在各个标签下的概率, 比如图像分类等.



线性回归基础

模型定义

以下以房价预测为例, 介绍线性回归的基本要素. 这里我们设: 真实房价为 y \Large y y, 这个值取决于面积 x 1 \Large x_1 x1, 以及房龄 x 2 \Large x_2 x2, 由此我们可以确定这个二元函数为:
y = f ( x 1 , x 2 ) = A 1 x 1 + A 2 x 2 + B \Large y = f(x_1, x_2)=A_1x_1+A_2x_2+B y=f(x1,x2)=A1x1+A2x2+B
这里我们再定义预测的房价为 y ^ \Large \hat{y} y^, 以及训练函数 f ^ \Large \hat{f} f^ 如下:
y ^ = f ^ ( x 1 , x 2 ) = ω 1 x 1 + ω 2 x 2 + b \Large \hat{y} = \hat{f}(x_1, x_2)=\omega_1x_1+\omega_2x_2+b y^=f^(x1,x2)=ω1x1+ω2x2+b
其中 ω 1 , ω 2 \Large \omega_1, \omega_2 ω1,ω2 为权重(weights), b \Large b b 为偏差(bias), y ^ \Large \hat{y} y^ 为预测的房价, 均为标量, 显然 y ^ \Large \hat{y} y^ y \Large y y 之间是存在误差的, 这个到后面再讨论.

所有这些变量都称为参数(parameter), 理想情况下最好能由算法自动维护更新, 而不是依靠人为的改动.


模型训练

定义好模型之后, 接下来需要设计算法, 能够让计算机自行找到合适的参数值, 使得 y ^ \Large \hat{y} y^ y \Large y y 之间的误差尽可能地小. 这个过程称之为模型训练(model training), 涉及下面三个步骤:

训练数据

首先我们需要事先收集一系列真实有效的数据, 这些数据是不允许任何改动的. 我们希望在这个数据上面训练模型参数, 来使得预测值和真实值之间的误差最小. 这个数据集称为训练集(training set), 其中的每一栋房屋称为一个样本(sample), 其真实房价称为标签(label), 决定标签的两个因素 x 1 , x 2 \Large x_1, x_2 x1,x2叫做特征(feature).

假设我们采集的样本数为 n \Large n n, 索引为 i \Large i i 的样本的特征为 x 1 ( i ) \Large x_1^{(i)} x1(i) x 2 ( i ) \Large x_2^{(i)} x2(i) , 对应标签为 y ( i ) \Large y^{(i)} y(i), 那么我们分别得到真实的房价函数和预测的房价函数分别如下:
y ( i ) = f ( x 1 ( i ) , x 2 ( i ) ) = A 1 x 1 ( i ) + A 2 x 2 ( i ) + B y ^ ( i ) = f ^ ( x 1 ( i ) , x 2 ( i ) ) = ω 1 x 1 ( i ) + ω 2 x 2 ( i ) + b \Large y^{(i)} = f(x_1^{(i)}, x_2^{(i)})=A_1x_1^{(i)}+A_2x_2^{(i)}+B \\ \Large \hat{y}^{(i)} = \hat{f}(x_1^{(i)}, x_2^{(i)})=\omega_1x_1^{(i)}+\omega_2x_2^{(i)}+b y(i)=f(x1(i),x2(i))=A1x1(i)+A2x2(i)+By^(i)=f^(x1(i),x2(i))=ω1x1(i)+ω2x2(i)+b

损失函数

这里我们开始处理误差. 通常情况下我们会选择一个非负数作为误差, 而且误差越小越好, 理想情况为0.

处理误差的方法有很多, 这里我们采用平方损失(square loss), 即预测值和真实值之差的平方作为误差函数(loss function)

我们定义索引为 i \Large i i 的样本的误差为:
ε ( i ) ( ω 1 , ω 2 , b ) = 1 2 ( y ^ ( i ) − y ( i ) ) 2 \Large \varepsilon^{(i)}(\omega_1, \omega_2, b) = \frac{1}{2}(\hat{y}^{(i)}-y^{(i)})^2 ε(i)(ω1,ω2,b)=21(y^(i)y(i))2
其中的常数 1 2 \Large \frac{1}{2} 21 只是为了对误差求导后的系数为1, 除此之外对算法没有太大的帮助. 对于给定的训练集, 误差函数只与模型参数有关.

通常, 我们用训练集中, 所有样本的误差的算术平均来衡量模型的质量, 即
ε ( ω 1 , ω 2 , b ) = 1 n ∑ i = 1 n ε ( i ) ( ω 1 , ω 2 , b ) = 1 2 n ∑ i = 1 n ( y ^ ( i ) − y ( i ) ) 2 \Large \varepsilon(\omega_1, \omega_2, b) = \frac{1}{n}\sum_{i=1}^{n}\varepsilon^{(i)}(\omega_1, \omega_2, b) = \frac{1}{2n}\sum_{i=1}^{n}(\hat{y}^{(i)}-y^{(i)})^2 ε(ω1,ω2,b)=n1i=1nε(i)(ω1,ω2,b)=2n1i=1n(y^(i)y(i))2
最后, 可以得出一组参数 ω 1 ∗ , ω 2 ∗ , b ∗ \Large \omega_1^*, \omega_2^*, b^* ω1,ω2,b, 使得上述样本平均损失最小:
ω 1 ∗ , ω 2 ∗ , b ∗ = arg ⁡ ω 1 , ω 2 , b min ⁡ ε ( ω 1 , ω 2 , b ) \Large \omega_1^*, \omega_2^*, b^* = \mathop{\arg}\limits_{\omega_1, \omega_2, b}\min \varepsilon(\omega_1, \omega_2, b) ω1,ω2,b=ω1,ω2,bargminε(ω1,ω2,b)

优化模型

当模型和损失函数形式较为简单时, 上面的误差最小化问题的解可以直接用公式表达出来. 这类解叫作解析解(analytical solution). 这里使用的线性回归和平方误差刚好属于这个范畴.

然而, 大多数深度学习模型并没有解析解, 只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值. 这类解叫作数值解(numerical solution)

在优化算法中, 最常用的是小批量随机梯度下降(mini-batch stochastic gradient descent) 的方法, 步骤如下:

  1. 指定或随机选取一组模型参数作为初值

  2. 对参数进行多次迭代, 每次迭代中, 依次执行:

    2.1 随机均匀采样一个由固定数目训练集样本组成的小批量(mini-batch) β \Large \beta β

    2.2 求 β \Large \beta β 中数据样本的平均损失, 再对模型参数求导

    2.3 用此结果与预先设定的一个正数 η \Large \eta η (学习率learning rate)的乘积, 作为模型参数在本次迭代中的减小量

    2.4 更新模型参数

  3. 重复2, 直到样本的平均误差小于给定的一个阈值

在这里, 模型的每个参数将做如下迭代:
ω 1 ← ω 1 − η ∥ β ∥ ∑ i ∈ β ∂ ε ( i ) ( ω 1 , ω 2 , b ) ∂ ω 1 = ω 1 − η ∥ β ∥ ∑ i ∈ β x 1 ( i ) ( ω 1 x 1 ( i ) + ω 2 x 2 ( i ) + b − y ( i ) ) \omega_1 \gets \omega_1-\frac{\eta}{\parallel \beta \parallel} \sum_{i \in \beta} \frac{\partial \varepsilon^{(i)}(\omega_1, \omega_2, b)}{\partial \omega_1} = \omega_1-\frac{\eta}{\parallel \beta \parallel} \sum_{i \in \beta}x_1^{(i)} (\omega_1x_1^{(i)}+\omega_2x_2^{(i)}+b-y^{(i)}) ω1ω1βηiβω1ε(i)(ω1,ω2,b)=ω1βηiβx1(i)(ω1x1(i)+ω2x2(i)+by(i))
ω 2 ← ω 2 − η ∥ β ∥ ∑ i ∈ β ∂ ε ( i ) ( ω 1 , ω 2 , b ) ∂ ω 2 = ω 2 − η ∥ β ∥ ∑ i ∈ β x 2 ( i ) ( ω 1 x 1 ( i ) + ω 2 x 2 ( i ) + b − y ( i ) ) \omega_2 \gets \omega_2-\frac{\eta}{\parallel \beta \parallel} \sum_{i \in \beta} \frac{\partial \varepsilon^{(i)}(\omega_1, \omega_2, b)}{\partial \omega_2} = \omega_2-\frac{\eta}{\parallel \beta \parallel} \sum_{i \in \beta}x_2^{(i)} (\omega_1x_1^{(i)}+\omega_2x_2^{(i)}+b-y^{(i)}) ω2ω2βηiβω2ε(i)(ω1,ω2,b)=ω2βηiβx2(i)(ω1x1(i)+ω2x2(i)+by(i))
b ← b − η ∥ β ∥ ∑ i ∈ β ∂ ε ( i ) ( ω 1 , ω 2 , b ) ∂ b = b − η ∥ β ∥ ∑ i ∈ β ( ω 1 x 1 ( i ) + ω 2 x 2 ( i ) + b − y ( i ) ) b \gets b-\frac{\eta}{\parallel \beta \parallel} \sum_{i \in \beta} \frac{\partial \varepsilon^{(i)}(\omega_1, \omega_2, b)}{\partial b} = b-\frac{\eta}{\parallel \beta \parallel} \sum_{i \in \beta} (\omega_1x_1^{(i)}+\omega_2x_2^{(i)}+b-y^{(i)}) bbβηiβbε(i)(ω1,ω2,b)=bβηiβ(ω1x1(i)+ω2x2(i)+by(i))
上式中, ∥ β ∥ \Large \parallel \beta \parallel β 表示每个小批量样本的个数, 即批量大小(batch size), η \Large \eta η 称为学习率(learning rate). 这些需要人为事先输入的参数称为超参数(hyperpatameter), 我们所说的调参, 是针对超参数而言的, 而不是对参数进行调整.



从零开始实现

为了加深理解, 这里只使用tf中的自动求梯度接口GradientTape(), 剩下的部分完全自定义实现.

定义超参数

首先我们定义几个全局变量作为超参数, 整个程序只有测试集的这几个参数可以调整. 如下

"""
训练集
"""
true_w = []
true_b = 0
features = []
labels = []
num_inputs = 2

"""
测试集
"""
# 权重初始化为均值为0,标准差为0。01的正态随机数
w = tf.Variable(tf.random.normal((num_inputs, 1), stddev=0.01))  
b = tf.Variable(tf.zeros((1,)))  # 偏差初始化为0
lr = 0.03  # 学习率
batch_size = 10  # 批量大小
num_epochs = 20  # 迭代周期

创建训练集

接着, 我们需要生成训练集, 这里我们使用tf的random模块生成正态随机矩阵, 并加入标准差为0.01的噪声.

设训练数据集样本数为1000, 输入个数(特征数)为2. 给定随机生成的批量样本特征 X ∈ R 1000 × 2 \Large X \in R^{1000 \times 2} XR1000×2. 调用时, 令w1=2, w2=-3.4, b=4.2,分别表示真实的权重和偏差. 最后需要加入一点点噪声 ε \Large \varepsilon ε, 令其服从均值为0, 标准差为0.01的正态分布. 由此我们得到了下面的函数:

def getTrainingData(w1, w2, b_, exampleNum=1000):
    """
    产生训练集
    :param w1: w1
    :param w2: w2
    :param b_:  b_
    :param exampleNum: 样本个数
    """
    global true_w, true_b, features, labels, num_inputs
    num_examples = exampleNum
    true_w = [w1, w2]
    true_b = b_
    features = tf.random.normal((num_examples, num_inputs), stddev=1)
    labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
    labels += tf.random.normal(labels.shape, stddev=0.01)

最后得到的feature是一个(1000, 2)的矩阵, labels是一个(1000, 1)的矩阵

这里可以使用print()输出一下生成的训练集, 推荐使用更直观的三维散点图, 如下:
在这里插入图片描述


批量读取数据

在训练模型的时候, 我们需要随机读取一小批量的样本, 这个函数以迭代器的方式, 每次返回batch_size个随机样本的特征和标签

def getBatch():
    """
    返回一个迭代器, 元素为随机选取的batch_size个测试集
    """
    global features, labels, batch_size
    num_examples = len(features)
    # 分片索引
    indices = list(range(num_examples))
    # 打乱顺序
    random.shuffle(indices)
    for i in range(0, num_examples, batch_size):
        # 随机选取并返回
        j = indices[i: min(i + batch_size, num_examples)]
        yield tf.gather(features, axis=0, indices=j), tf.gather(labels, axis=0, indices=j)

定义模型

ω \Large \omega ω 为权重矩阵, X \Large X X 为特征矩阵, b \Large b b 为偏差, 那么矢量形式的模型表达式为:
y ^ = X ω + b \Large \hat{y} = X\omega + b y^=Xω+b
写成代码就是下面的函数:

def linearRegression(X):
  """
  回归方程
  :param X:
  :return:
  """
  global w, b
  return tf.matmul(X, w) + b

定义损失函数

为了模块化设计, 损失函数最好单独定义一个函数, 这样可以方便地比较不同损失函数的效果.

这里使用上面介绍的差平方法, 如下:

def getLoss(y_hat, y):
    """
    返回预测值和真实值的误差
    :param y_hat: 预测值
    :param y: 真实值
    :return: 两者的误差(差平方法)
    """
    return (y_hat - tf.reshape(y, y_hat.shape)) ** 2 / 2

需要注意的是, 这里需要将真实值y变形成预测值 y_hat 的形状.


定义优化算法

以下的sgd()函数实现了之前介绍的小批量随机梯度下降方法. 它通过不断迭代模型参数来优化损失函数. 由于这里自动求梯度模块计算得到的梯度是一个批量样本的梯度和, 因此需要将它除以批量大小来得到平均值.

def sgd(params, grads):
    """
    小批量随机梯度下降
    模型优化算法, 不断迭代模型参数来优化损失函数.
    :param params:
    :param grads: 梯度
    """
    global lr
    for i, param in enumerate(params):
        # 这里自动求梯度计算得到的是一批量样本的梯度和, 需要除以批量大小来得到平均值
        param.assign_sub(lr * grads[i] / batch_size)

训练模型

最后, 我们需要进行多次的模型训练. 在每次训练中, 需要根据当前读取的小批量数据样本(特征x和标签y), 通过调用反向函数tf.gradients()来计算小批量随机梯度, 并通过优化算法sgd()迭代, 来优化模型参数.

在一个迭代周期(epoch)中, 将完整遍历一遍getBatch()函数, 并对训练集中所有样本都是用一次.

在实践中, 大多数超参数都需要通过人为反复试错来不断调节, 越长的迭代周期显然能够得到更低的误差, 但是训练的时间无疑也会增加.

def train():
    """
    迭代训练
    """

    global lr, features, labels, num_epochs, w, b
    net = linearRegression
    loss = getLoss

    for epoch in range(num_epochs):
        for X, y in getBatch():
            with tf.GradientTape() as t:
                t.watch([w, b])
                loss_tmp = loss(net(X), y)
            grads = t.gradient(loss_tmp, [w, b])
            sgd([w, b], grads)
        train_l = loss(net(features), labels)
        print('epoch:{}, loss:{}.'.format(epoch + 1, tf.reduce_mean(train_l)))
    print('true_w:{}, w:{}'.format(true_w, w))
    print('true_b:{}, b:{}'.format(true_b, b))

运行结果

main()函数中, 执行以下代码:

if __name__ == '__main__':
    getTrainingData(2, -3.4, 4.2)
    showTrainingData()
    train()
    showTrainingResult()

得到以下的两张三维图和输出:
在这里插入图片描述

在这里插入图片描述

# console output:
epoch:1, loss:0.04223724454641342.
epoch:2, loss:0.00016849979874677956.
epoch:3, loss:4.922624066239223e-05.
epoch:4, loss:4.8856465582503006e-05.
epoch:5, loss:4.881443965132348e-05.
epoch:6, loss:4.8818328650668263e-05.
epoch:7, loss:4.8800840886542574e-05.
epoch:8, loss:4.8751055146567523e-05.
epoch:9, loss:4.892029392067343e-05.
epoch:10, loss:4.892560900771059e-05.
epoch:11, loss:4.879352491116151e-05.
epoch:12, loss:4.876883394899778e-05.
epoch:13, loss:4.875730519415811e-05.
epoch:14, loss:4.879812695435248e-05.
epoch:15, loss:4.894009907729924e-05.
epoch:16, loss:4.900624480796978e-05.
epoch:17, loss:4.8875805077841505e-05.
epoch:18, loss:4.8828169383341447e-05.
epoch:19, loss:4.8838577640708536e-05.
epoch:20, loss:4.893250661552884e-05.
true_w:[2, -3.4], w:<tf.Variable 'Variable:0' shape=(2, 1) dtype=float32, numpy=
array([[ 2.0004492],
       [-3.399655 ]], dtype=float32)>
true_b:4.2, b:<tf.Variable 'Variable:0' shape=(1,) dtype=float32, numpy=array([4.200075], dtype=float32)>

完整代码

# encoding: utf-8

"""
@author:   Hengyu Jiang
@file:     LinearRegression.py
@project:  tensorflowCourse
@time:     2020/6/19 11:38 下午
"""


"""
-------------------------required environment-------------------------
numpy == 1.18.5
tensorflow == 2.0.0b0
matplotlib == 3.2.1

"""


import random
import numpy as np
import tensorflow as tf
from matplotlib import pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['axes.unicode_minus'] = False

"""
训练集
"""
true_w = []
true_b = 0
features = []
labels = []
num_inputs = 2

"""
测试集
"""
w = tf.Variable(tf.random.normal((num_inputs, 1), stddev=0.01))  # 权重初始化为均值为0,标准差为0。01的正态随机数
b = tf.Variable(tf.zeros((1,)))  # 偏差初始化为0
lr = 0.03  # 学习率
batch_size = 10  # 批量大小
num_epochs = 20  # 迭代周期


def showTrainingData():
    """
    以三维散点图的形式画出训练集
    """
    x = features[:, 0]
    y = features[:, 1]
    z = labels
    ax = plt.subplot(projection='3d')
    ax.set_title('训练集')
    ax.scatter(x, y, z, c='blue')
    ax.set_xlabel('x1')
    ax.set_ylabel('x2')
    ax.set_zlabel('y')
    plt.show()


def showTrainingResult():
    """
    以三维图的形式显示回归结果
    """
    global features, labels, w, b
    x = features[:, 0]
    y = features[:, 1]

    ax = plt.axes(projection='3d')
    ax.scatter(x, y, labels, c='blue')

    X, Y = np.meshgrid(x, y)  # 创建网格
    Z = function(X, Y)
    ax.plot_surface(X, Y, Z, cmap='gray')
    ax.set_xlabel('x1')
    ax.set_ylabel('x2')
    ax.set_zlabel('y')
    plt.show()


def function(x, y):
    """
    二元一次函数, 计算w1*x1 + w2*x2 + b
    :param x:
    :param y:
    :return:
    """
    global w, b
    return w[0] * x + w[1] * y + b


def getTrainingData(w1, w2, b_, exampleNum=1000):
    """
    产生训练集
    :param w1: w1
    :param w2: w2
    :param b_:  b_
    :param exampleNum: 样本个数
    """
    global true_w, true_b, features, labels, num_inputs
    num_examples = exampleNum
    true_w = [w1, w2]
    true_b = b_
    features = tf.random.normal((num_examples, num_inputs), stddev=1)
    labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
    labels += tf.random.normal(labels.shape, stddev=0.01)


def getBatch():
    """
    返回一个迭代器, 元素为随机选取的batch_size个测试集
    """
    global features, labels, batch_size
    num_examples = len(features)
    # 分片索引
    indices = list(range(num_examples))
    # 打乱顺序
    random.shuffle(indices)
    for i in range(0, num_examples, batch_size):
        # 随机选取并返回
        j = indices[i: min(i + batch_size, num_examples)]
        yield tf.gather(features, axis=0, indices=j), tf.gather(labels, axis=0, indices=j)


def linearRegression(X):
    """
    回归方程
    :param X:
    :return:
    """
    global w, b
    return tf.matmul(X, w) + b


def getLoss(y_hat, y):
    """
    返回预测值和真实值的误差
    :param y_hat: 预测值
    :param y: 真实值
    :return: 两者的误差(差平方法)
    """
    return (y_hat - tf.reshape(y, y_hat.shape)) ** 2 / 2


def sgd(params, grads):
    """
    小批量随机梯度下降
    模型优化算法, 不断迭代模型参数来优化损失函数.
    :param params:
    :param grads: 梯度
    """
    global lr
    for i, param in enumerate(params):
        # 这里自动求梯度计算得到的是一批量样本的梯度和, 需要除以批量大小来得到平均值
        param.assign_sub(lr * grads[i] / batch_size)


def train():
    """
    迭代训练
    """

    global lr, features, labels, num_epochs, w, b
    net = linearRegression
    loss = getLoss

    for epoch in range(num_epochs):
        for X, y in getBatch():
            with tf.GradientTape() as t:
                t.watch([w, b])
                loss_tmp = loss(net(X), y)
            grads = t.gradient(loss_tmp, [w, b])
            sgd([w, b], grads)
        train_l = loss(net(features), labels)
        print('epoch:{}, loss:{}.'.format(epoch + 1, tf.reduce_mean(train_l)))
    print('true_w:{}, w:{}'.format(true_w, w))
    print('true_b:{}, b:{}'.format(true_b, b))


if __name__ == '__main__':
    getTrainingData(2, -3.4, 4.2)
    showTrainingData()
    train()
    showTrainingResult()


简洁实现

上一节中, 只调用了tensorflow的一个梯度模块, 其余部分完全由自定义实现, 工程量比较大. 这一节将借助tensorflow, 让实现更加简洁和便利.

定义超参数和训练集

num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
lr = 0.002  # 学习率
batch_size = 10  # 批量大小
num_epochs = 30  # 迭代周期

features = tf.random.normal(shape=(num_examples, num_inputs), stddev=1)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += tf.random.normal(labels.shape, stddev=0.01)


读取数据

dataset = tf.data.Dataset.from_tensor_slices((features, labels))
dataset = dataset.shuffle(buffer_size=num_examples) # 随机打乱顺序
dataset = dataset.batch(batch_size) # 随机选取


定义模型

tensorflow2.0内置了Keras,在构造网络之前, 需要先定义一个模型model,它是一个Sequential实例, 也就是一个串联各个网络层的容器.

在构造模型的时候, 需要依次向其中添加层layer. 线性回归中, 输入层到输出层之间等效为一个层, 即全连接层keras.layers.Dense(), 需要指定

model = keras.Sequential()
model.add(layers.Dense(1, kernel_initializer=init.RandomNormal(stddev=0.01)))


定义损失函数

Tensoflowlosses模块提供了各种损失函数和自定义损失函数的基类,并直接使用它的均方误差损失作为模型的损失函数, 这里直接选择均差方函数即可.

loss = losses.MeanSquaredError()


定义优化算法

同上, Tensoflow 也提供了很多常见的优化算法, 比如SGD, Adam, RMSProp等, 通过模块tensorflow.keras.optimizers可直接调用

optimizer = optimizers.SGD(learning_rate=lr)


编译并训练模型

# input_shape指定特征的数量,这里特征是一个一维向量[w1, w2], 注意(num_inputs,)里面的逗号,不可少
model = keras.Sequential([
    layers.Dense(1, input_shape=(num_inputs,))
])
# 编译模型
model.compile(optimizer=optimizer,
              loss=loss,
              metrics=['accuracy'])
# 输出模型信息
model.summary()
# 开始迭代
model.fit(features, labels, epochs=num_epochs, batch_size=batch_size)


完整代码

# encoding: utf-8

"""
@author:   Hengyu Jiang
@file:     LinearRegressionTf.py
@project:  tensorflowCourse
@time:     2020/6/29 6:26 下午

-------------------------required environment-------------------------
-   numpy == 1.18.5                                                  -
-   tensorflow == 2.0.0b0                                            -
-   matplotlib == 3.2.1                                              -
----------------------------------------------------------------------

"""

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow import losses
from tensorflow.keras import layers
from matplotlib import pyplot as plt


"""
解决win平台下, pyplt的中文乱码问题(mac端需手动下载SimHei字体后才能生效)
"""
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['axes.unicode_minus'] = False

"""
超参数和训练集
"""
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
lr = 0.002  # 学习率
batch_size = 10  # 批量大小
num_epochs = 15  # 迭代周期

features = tf.random.normal(shape=(num_examples, num_inputs), stddev=1)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += tf.random.normal(labels.shape, stddev=0.01)

"""
读取batch
"""
dataset = tf.data.Dataset.from_tensor_slices((features, labels))
dataset = dataset.shuffle(buffer_size=num_examples)  # 随机打乱顺序
dataset = dataset.batch(batch_size)  # 随机选取

"""
定义损失函数
"""
loss = losses.MeanSquaredError()

"""
定义优化算法
"""
optimizer = tf.keras.optimizers.SGD(lr)


def function(w1, w2, x1, x2, b):
    """
    二元一次函数, 计算w1*x1 + w2*x2 + b
    :param w1:
    :param w2:
    :param x1:
    :param x2:
    :return:
    """
    return w1*x1 + w2*x2 + b


def showTrainingData():
    """
    以三维散点图的形式画出训练集
    """
    x1 = features[:, 0]
    x2 = features[:, 1]
    z = labels
    ax = plt.subplot(projection='3d')
    ax.set_title('训练集')
    ax.scatter(x1, x2, z, c='blue')
    ax.set_xlabel('x1')
    ax.set_ylabel('x2')
    ax.set_zlabel('y')
    plt.show()


def showTrainingResult(params):
    """
    以三维图的形式显示回归结果
    """
    global features, labels
    x1 = features[:, 0]
    x2 = features[:, 1]

    ax = plt.axes(projection='3d')
    ax.scatter(x1, x2, labels, c='blue')

    X1, X2 = np.meshgrid(x1, x2)  # 创建网格
    Z = function(params[0][0], params[0][1], X1, X2, params[1][0])
    ax.plot_surface(X1, X2, Z, cmap='gray')
    ax.set_xlabel('x1')
    ax.set_ylabel('x2')
    ax.set_zlabel('y')
    plt.show()


"""
定义并训练模型
"""
# input_shape指定特征的数量,这里特征是一个一维向量[w1, w2], 注意(num_inputs,)里面的逗号,不可少
model = keras.Sequential([
    layers.Dense(1, input_shape=(num_inputs,))
])
# 编译模型
model.compile(optimizer=optimizer,
              loss=loss,
              metrics=['accuracy'])
# 输出模型信息
model.summary()

# 画出训练数据
showTrainingData()

# 开始迭代
model.fit(features, labels, epochs=num_epochs, batch_size=batch_size)

print('true_w:{}, train_w:{}'.format(true_w, model.get_weights()[0]))
print('true_b:{}, train_b:{}'.format(true_b, model.get_weights()[1][0]))

# 画出训练结果平面
showTrainingResult(model.get_weights())
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值