TensorFlow深度学习实战(6)——回归分析详解
TensorFlow深度学习实战(6)——回归分析详解
- 0. 前言
- 1. 回归分析简介
- 2. 线性回归
- 2.1 简单线性回归
- 2.2 多重线性回归
- 2.3 多元线性回归
- 3. 构建基于线性回归的神经网络
- 3.1 使用 TensorFlow 进行简单线性回归
- 3.2 使用 TensorFlow 进行多元线性回归和多重线性回归
- 小结
- 系列链接
0. 前言
回归分析 (Regression Analysis
) 是一种统计方法,用于研究一个或多个自变量(输入变量)与因变量(输出变量)之间的关系,通过建立数学模型来预测或解释因变量的变化,理解变量之间的相互作用或进行预测。
1. 回归分析简介
回归分析通过学习给定一组因变量和自变量之间的关系从数据中进行预测。只要涉及到分析两个或多个事物之间的关系时,回归分析都有其应用。
以房价估计为例,房价可能受到许多因素的影响:房间数量、房屋面积、地理位置、设施的可用性、停车位等等,回归分析可以找出这些因素与房价之间的数学关系。
简化此问题,假设房屋价格仅由房屋面积决定。利用回归分析,我们可以确定房屋面积(自变量:不依赖于其他变量的变量)与房价(因变量:依赖于一个或多个自变量的变量)之间的关系。之后,只需知道房屋面积,我们就可以利用这种关系来预测房屋的价格。在机器学习中,自变量通常作为模型的输入,而因变量则是模型的输出。
根据自变量的数量、因变量的数量以及关系类型,回归分析有许多不同的类型。回归分析的两个重要组成部分是自变量和因变量之间的关系,以及不同自变量对因变量的影响力。接下来,我们将学习线性回归技术。
2. 线性回归
线性回归已有200多年历史,是最广为人知的建模技术之一。线性回归假设输入变量
X
X
X 与输出变量
Y
Y
Y 之间存在线性关系,基本思想是构建一个模型,利用训练数据来预测给定输入的输出,使得预测输出
Y
^
\hat{Y}
Y^ 尽可能接近观察到的训练数据输出
Y
Y
Y。为预测值
Y
^
\hat{Y}
Y^ 构建一个线性方程,形式如下:
Y
^
=
W
T
X
+
b
\hat{Y}=W^TX+b
Y^=WTX+b
其中
X
=
{
x
1
,
x
2
,
.
.
.
,
x
n
}
X=\{x_1,x_2,...,x_n\}
X={x1,x2,...,xn} 是包含
n
n
n 个变量的输入,
W
=
{
w
1
,
w
2
,
.
.
.
.
,
w
n
}
W=\{w_1,w_2,....,w_n\}
W={w1,w2,....,wn} 是线性系数,
b
b
b 是偏置项。可以将上述方程扩展为:
Y
^
=
∑
i
=
1
x
i
w
i
+
b
\hat{Y}=\sum_{i=1}x_iw_i+b
Y^=i=1∑xiwi+b
偏置项提供了一个选项来调整数据以获得更好的拟合。对于输入样本
i
i
i,观察值
Y
Y
Y 与预测值
Y
^
\hat Y
Y^ 之间的误差为:
e
i
=
Y
i
−
Y
^
i
e_i=Y_i-\hat Y_i
ei=Yi−Y^i
目标是找到最佳的系数
W
W
W 和偏置项
b
b
b 的估计值,使得观察值
Y
Y
Y 与预测值
Y
^
\hat Y
Y^ 之间的误差最小化。接下来,我们通过实际示例来进一步理解回归分析。
2.1 简单线性回归
如果只考虑一个自变量和一个因变量,就是简单线性回归。以房价预测为例,房屋面积
A
A
A 是自变量,房屋价格
Y
Y
Y 是因变量。我们希望找到预测价格
Y
^
\hat Y
Y^ 与
A
A
A 之间的线性关系,形式如下:
Y
^
=
A
W
+
b
\hat Y=AW+b
Y^=AW+b
其中
b
b
b 是偏置项。因此,我们需要确定
W
W
W 和
b
b
b,使得价格
Y
Y
Y 和预测价格
Y
^
\hat Y
Y^ 之间的误差最小化。用于估计
W
W
W 和
b
b
b 的标准方法称为最小二乘法,即试图最小化误差平方和
S
S
S,具体到房价预测问题,表达式为:
S
(
W
,
b
)
=
∑
i
=
1
N
(
Y
i
−
Y
^
i
)
2
=
∑
i
=
1
N
(
Y
i
−
A
i
W
−
b
)
2
S(W,b)=\sum_{i=1}^{N}(Y_i-\hat Y_i)^2=\sum_{i=1}^{N}(Y_i-A_iW-b)^2
S(W,b)=i=1∑N(Yi−Y^i)2=i=1∑N(Yi−AiW−b)2
目标是估计回归系数
W
W
W 和
b
b
b,使得
S
S
S 最小化。利用函数的导数在极小值处为 0
的性质,可以得到以下两个方程:
∂
S
∂
W
=
−
2
∑
i
=
1
N
(
Y
i
−
A
i
W
−
b
)
A
i
=
0
∂
S
∂
b
=
−
2
∑
i
=
1
N
(
Y
i
−
A
i
W
−
b
)
=
0
\frac {\partial S} {\partial W}=-2\sum_{i=1}^{N}(Y_i-A_iW-b)A_i=0\\ \frac {\partial S} {\partial b}=-2\sum_{i=1}^{N}(Y_i-A_iW-b)=0
∂W∂S=−2i=1∑N(Yi−AiW−b)Ai=0∂b∂S=−2i=1∑N(Yi−AiW−b)=0
根据以上两个方程可以求解出两个未知数。首先,将第二个方程中的求和部分展开:
∑
i
=
1
N
Y
i
−
∑
i
=
1
N
A
i
W
−
∑
i
=
1
N
b
=
0
\sum_{i=1}^{N}Y_i-\sum_{i=1}^{N}A_iW-\sum_{i=1}^{N}b=0
i=1∑NYi−i=1∑NAiW−i=1∑Nb=0
观察左侧的最后一项,它只是常数
b
b
b 求了
N
N
N 次和。因此,可以重写为:
∑
i
=
1
N
Y
i
−
W
∑
i
=
1
N
A
i
−
N
b
=
0
\sum_{i=1}^{N}Y_i-W\sum_{i=1}^{N}A_i-Nb=0
i=1∑NYi−Wi=1∑NAi−Nb=0
重新排列项,得到:
b
=
1
N
∑
i
=
1
N
Y
i
−
W
N
∑
i
=
1
N
A
i
b=\frac 1N\sum_{i=1}^{N}Y_i-\frac WN\sum_{i=1}^{N}A_i
b=N1i=1∑NYi−NWi=1∑NAi
右侧的两项可以分别用
Y
‾
\overline Y
Y (平均价格)和
A
‾
\overline A
A (平均面积)来替代,于是得到:
b
=
Y
‾
−
W
A
‾
b=\overline Y-W\overline A
b=Y−WA
类似地,展开
S
S
S 关于权重
W
W
W 的偏导数方程:
∑
i
=
1
N
(
Y
i
A
i
−
A
i
2
W
−
b
A
i
)
=
0
\sum_{i=1}^{N}(Y_iA_i-A_i^2W-bA_i)=0
i=1∑N(YiAi−Ai2W−bAi)=0
将偏置项
b
b
b 的表达式代入:
∑
i
=
1
N
(
Y
i
A
i
−
A
i
2
W
−
(
Y
‾
−
W
A
‾
)
A
i
)
=
0
\sum_{i=1}^{N}(Y_iA_i-A_i^2W-(\overline Y-W\overline A)A_i)=0
i=1∑N(YiAi−Ai2W−(Y−WA)Ai)=0
重新排列方程:
∑
i
=
1
N
(
Y
i
A
i
−
Y
‾
A
i
)
−
W
∑
i
=
1
N
(
A
i
2
−
A
‾
A
i
)
=
0
\sum_{i=1}^{N}(Y_iA_i-\overline YA_i)-W\sum_{i=1}^{N}(A_i^2-\overline AA_i)=0
i=1∑N(YiAi−YAi)−Wi=1∑N(Ai2−AAi)=0
通过平均值的定义,可以得到权重 W 的值:
W
=
∑
i
=
1
N
A
i
(
Y
i
−
Y
‾
)
∑
i
=
1
N
(
A
i
2
−
A
‾
A
i
)
W=\frac{\sum_{i=1}^{N}A_i(Y_i-\overline Y)}{\sum_{i=1}^{N}(A_i^2-\overline AA_i)}
W=∑i=1N(Ai2−AAi)∑i=1NAi(Yi−Y)
其中
Y
‾
\overline Y
Y 和
A
‾
\overline A
A 分别是平均价格和平均面积,接下来,我们在一些简单的数据样本上实现简单线性回归方法。
(1) 导入所需的库,使用 NumPy
、pandas
和 Matplotlib
:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
(2) 接下来,生成具有线性关系的随机数据,通过添加随机噪声使数据更真实。可以看到这两个变量(自变量 area
和因变量 price
)遵循线性关系:
area = 2.5 * np.random.randn(100) + 25
price = 25 * area + 5 + np.random.randint(20,50, size = len(area))
data = np.array([area, price])
data = pd.DataFrame(data = data.T, columns=['area','price'])
plt.scatter(data['area'], data['price'])
plt.show()
(3) 使用上述方程计算两个回归系数,可以看到结果非常接近我们模拟的线性关系:
W = sum(area*(price-np.mean(price))) / sum(area**2-(area*np.mean(area)))
b = np.mean(price) - W*np.mean(area)
print("The regression coefficients are", W,b)
# The regression coefficients are 24.618504156519812 48.05013397684468
(4) 尝试使用得到的权重和偏置值来预测新的房屋价格:
y_pred = W * area + b
(5) 接下来,绘制预测的价格以及实际价格,可以看到预测价格与面积之间存在线性关系:
plt.plot(area, y_pred, color='red',label="Predicted Price")
plt.scatter(data['area'], data['price'], label="Training Data")
plt.xlabel("Area")
plt.ylabel("Price")
plt.legend()
plt.show()
从图中可以看出,预测值与实际房价呈相同的趋势。
2.2 多重线性回归
在大多数问题中,因变量依赖于多个自变量。多重线性回归寻找多个独立输入变量
X
X
X 和依赖输出变量
Y
Y
Y 之间的线性关系,使得它们满足以下形式的预测值
Y
^
\hat Y
Y^:
Y
^
=
W
T
X
+
b
\hat Y=W^TX+b
Y^=WTX+b
其中
X
=
{
x
1
,
x
2
,
.
.
.
,
x
n
}
X=\{x_1,x_2,...,x_n\}
X={x1,x2,...,xn} 是
n
n
n 个独立输入变量,
W
=
{
w
1
,
w
2
,
.
.
.
,
w
n
}
W=\{w_1,w_2,...,w_n\}
W={w1,w2,...,wn} 是线性系数,
b
b
b 是偏置项。
线性系数
W
W
W 使用最小二乘法估计,即最小化预测值
Y
^
\hat Y
Y^ 与观察值
Y
Y
Y 之间的平方差,尝试最小化损失函数(即平方误差,如果除以 n ,则为均方误差):
l
o
s
s
=
∑
i
(
Y
i
−
Y
^
i
)
2
loss=\sum_i(Y_i-\hat Y_i)^2
loss=i∑(Yi−Y^i)2
其中求和操作会遍历所有训练样本。我们将得到
n
+
1
n+1
n+1 个方程需要求解。更简单的选择是使用 TensorFlow
,下一节中,我们将使用 TensorFlow API
执行回归任务。
2.3 多元线性回归
有某些情况下,自变量可能会影响多个因变量。例如,我们要预测火箭的速度和二氧化碳排放量的情况,这两个变量将作为因变量,并且它们都受到燃料量、引擎类型、火箭机体等的影响。这就是多元线性回归的情况,数学上,多元回归模型可以表示为:
Y
^
i
j
=
w
0
j
+
∑
k
=
1
p
w
k
j
x
i
j
\hat Y_{ij} = w_{0j} + \sum_{k=1}^pw_{kj}x_{ij}
Y^ij=w0j+k=1∑pwkjxij
其中
i
∈
[
1
,
…
,
n
]
i \in [1, \ldots, n]
i∈[1,…,n] 表示输入样本的索引,
j
∈
[
1
,
…
,
m
]
j \in [1, \ldots, m]
j∈[1,…,m] 表示预测输出值的索引。
Y
^
i
j
\hat Y_{ij}
Y^ij 表示第
i
i
i 个输入样本对应的第
j
j
j 个预测输出值,
w
w
w 表示回归系数,
x
i
k
x_{ik}
xik 是第
i
i
i 个输入样本的第
k
k
k 个特征。在这种情况下,需要求解的方程数量为
n
×
m
n \times m
n×m。虽然我们可以使用矩阵来解这些方程,但该过程计算成本高昂,因为需要计算逆矩阵和行列式。更简单的方法是使用梯度下降法,以最小化平方误差和作为损失函数,并使用 TensorFlow API
中的优化器。
3. 构建基于线性回归的神经网络
在上一小节中,我们使用数学表达式来计算线性回归方程的系数。在本节中,我们将学习如何使用神经网络执行回归任务,并使用 TensorFlow
构建神经网络模型。
在使用神经网络进行回归之前,我们首先回顾一下神经网络的基本概念。简单地说,神经网络是许多人工神经元组成的网络,最简单的神经网络,即简单感知器,可以用数学方式表示为:
y
=
f
(
W
T
x
+
b
)
y=f(W^Tx+b)
y=f(WTx+b)
其中
f
f
f 是激活函数,如果我们将
f
f
f 设置为线性函数,那么上述表达式就类似于线性回归表达式。换句话说,我们也可以将神经网络称为函数逼近器,是一种广义的回归器。接下来,使用 TensorFlow
构建一个简单的神经网络回归器。
3.1 使用 TensorFlow 进行简单线性回归
我们已经学习了如何使用 TensorFlow 构建神经网络模型。在本节中,我们将使用 Sequential API
构建一个单层感知器(全连接神经网络),使用 Dense
类,根据给定其面积预测房屋价格。
(1) 首先,导入所需的库:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import tensorflow.keras as K
from tensorflow.keras.layers import Dense
(2) 接下来,生成数据:
area = 2.5 * np.random.randn(100) + 25
price = 25 * area + 5 + np.random.randint(20,50, size = len(area))
data = np.array([area, price])
data = pd.DataFrame(data = data.T, columns=['area','price'])
plt.scatter(data['area'], data['price'])
plt.show()
(3) 神经网络的输入应该进行归一化处理;这是因为输入会与权重相乘,如果输入值很大,乘积的结果也会很大,最终可能会超出计算机能够处理的最大值:
data = (data - data.min()) / (data.max() - data.min()) #Normalize
(4) 构建模型,由于需要构建简单的线性回归器,使用只有一个神经元的 Dense
层:
model = K.Sequential([Dense(1, input_shape = [1,], activation=None)])
model.summary()
(5) 训练模型,定义损失函数和优化器。损失函数定义了模型试图最小化的量,优化器决定了使用的最小化算法。此外,定义评价指标作为模型训练时要记录的量。使用 compile()
函数定义损失函数、优化器和评价指标:
model.compile(loss='mean_squared_error', optimizer='sgd')
(6) 模型定义好后,使用 fit()
函数进行训练。使用的批大小为 32
,使用 fit()
函数的 validation_split
参数将数据分割成训练和验证数据集:
model.fit(x=data['area'],y=data['price'], epochs=100, batch_size=32, verbose=1, validation_split=0.2)
(7) 训练了 100
个epochs后,训练数据的均方误差为 0.0012
,验证数据的均方误差为 0.0011
,使用 predict()
函数获取给定输入的预测值:
y_pred = model.predict(data['area'])
(8) 可视化预测数据和实际数据:
plt.plot(data['area'], y_pred, color='red',label="Predicted Price")
plt.scatter(data['area'], data['price'], label="Training Data")
plt.xlabel("Area")
plt.ylabel("Price")
plt.legend()
plt.show()
下图显示了预测数据与实际数据之间的关系图。可以看到,就像线性回归一样,得到了一个良好的线性拟合:
(9) 通过打印模型权重查看权重 W W W 和偏置 b b b:
print(model.weights)
可以看到权重 W = 1.1668608 W=1.1668608 W=1.1668608 和偏置 b = − 0.0682285 b=-0.0682285 b=−0.0682285。因此,使用线性回归,可以得到房屋价格与其面积之间的线性关系。
3.2 使用 TensorFlow 进行多元线性回归和多重线性回归
在上一节中,只有一个自变量,即房屋的面积,和一个因变量,即房屋的价格。然而,现实生活中的问题并不是那么简单;我们可能有多个自变量,并且需要预测多个因变量,这涉及解决多个方程。通过使用 TensorFlow
可以更容易解决问题,可以使用多个网络层创建深度神经网络,即应用多个函数逼近器:
f
(
x
)
=
f
L
(
f
L
−
1
(
…
f
1
(
x
)
)
)
f(x)=f_L(f_{L-1}(\ldots f_1(x)))
f(x)=fL(fL−1(…f1(x)))
在上述表达式中,如果
f
L
f_L
fL 是一个线性函数,那么添加多层神经网络并不能提高模型性能,使用非线性激活函数能够将神经网络应用于因变量和自变量,以非线性方式解决相关的回归问题。在本节中,我们将使用 TensorFlow
构建一个深度神经网络,在给定汽车气缸数、排量、加速度等信息的情况下,预测汽车的燃油效率。
(1) 首先,导入所需库。在上一小节中,使用 DataFrame
操作来对数据进行归一化。在本节中,使用 Normalization
层,Normalization
层将数据调整到均值为 0
、标准差为 1
;此外,由于有多个自变量,使用 Seaborn
来可视化不同变量之间的关系:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import tensorflow.keras as K
from tensorflow.keras.layers import Dense, Normalization
import seaborn as sns
(2) 从 UCI
机器学习库下载数据集:
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data'
column_names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight',
'acceleration', 'model_year', 'origin']
data = pd.read_csv(url, names=column_names,
na_values='?', comment='\t',
sep=' ', skipinitialspace=True)
(3) 数据包含八个特征:mpg
(每加仑英里数)、汽缸数、排量、马力、重量、加速度、型号年份和原产地。虽然车辆的原产地也会影响到燃油效率 mpg
,但本节中,我们只使用其它六个特征来预测 mpg
值。此外,丢弃包含 NaN
值的行:
data = data.drop('origin', axis=1)
print(data.isna().sum())
data = data.dropna()
(4) 将数据集分为训练集和测试集,将 392
个数据点中的 80%
作为训练数据,20%
作为测试数据集:
train_dataset = data.sample(frac=0.8, random_state=0)
test_dataset = data.drop(train_dataset.index)
(5) 接下来,使用Seaborn
的 pairplot
可视化不同变量之间的关系:
sns.pairplot(train_dataset[['mpg', 'cylinders', 'displacement','horsepower', 'weight', 'acceleration', 'model_year']], diag_kind='kde')
plt.show()
可以看到,mpg
(燃油效率)与其他变量都有关系,且是非线性的:
(6) 将变量分为输入变量和我们要预测的结果值:
train_dataset.describe().transpose()[['mean', 'std']]
train_features = train_dataset.copy()
test_features = test_dataset.copy()
train_labels = train_features.pop('mpg')
test_labels = test_features.pop('mpg')
(7) 使用 Normalization
层对数据进行标准化。需要注意的是,虽然我们将输入数据标准化为均值为 0
,标准差为 1
,但输出预测值 mpg
保持不变:
data_normalizer = Normalization(axis=1)
data_normalizer.adapt(np.array(train_features))
(8) 构建模型。模型有两个隐藏层,分别包含 64
个和 32
个神经元。对于隐藏层,使用 ReLU
作为激活函数,有助于逼近燃油效率与其他变量之间的非线性关系:
model = K.Sequential([
data_normalizer,
Dense(64, activation='relu'),
Dense(32, activation='relu'),
Dense(1, activation=None)
])
model.summary()
(9) 使用 Adam
优化器和均方误差损失函数编译模型:
model.compile(optimizer='adam', loss='mean_squared_error')
(10) 对模型训练 100
个 epochs
:
history = model.fit(x=train_features,y=train_labels, epochs=100, verbose=1, validation_split=0.2)
(11) 模型训练完成后,通过绘制损失曲线检查模型是否过拟合。随着训练 epoch
的增加,验证损失和训练损失接近,这表明模型的训练得当:
plt.plot(history.history['loss'], label='loss')
plt.plot(history.history['val_loss'], label='val_loss')
plt.xlabel('Epoch')
plt.ylabel('Error [MPG]')
plt.legend()
plt.show()
(12) 最后,比较在测试数据集上预测的燃油效率和真实的燃油效率。如果模型学习了输入和燃油效率之间的关系,预测值和真实值应该形成线性关系:
y_pred = model.predict(test_features).flatten()
plt.scatter(test_labels, y_pred)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
lims = [0, 50]
plt.plot(lims, lims)
plt.show()
(13) 绘制预测值与真实燃油效率之间的误差:
error = y_pred - test_labels
plt.hist(error, bins=30)
plt.xlabel('Prediction Error [MPG]')
plt.ylabel('Count')
plt.show()
如果想要进行多个预测,也就是处理多元(变量)回归问题,唯一的变化是最后一个 Dense
层的神经元数量将与要预测的变量数目相同。例如,需要通过学生的 SAT
成绩、出勤情况等,预测学生大学四年的 GPA
分数,那么输出层将有四个神经元。
小结
回归分析用于建立变量之间的数学模型,分析其关系,并进行预测。通过合理选择回归模型并确保假设条件成立,回归分析能够帮助决策和优化策略。本节中,首先介绍了线性回归,并用线性回归来预测简单的单变量房价情况,然后使用 TensorFlow
构建了简单和多重线性回归模型。
系列链接
TensorFlow深度学习实战(1)——神经网络与模型训练过程详解
TensorFlow深度学习实战(2)——使用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
TensorFlow深度学习实战(4)——正则化技术详解
TensorFlow深度学习实战(5)——神经网络性能优化技术详解