27、深度学习模型训练与逻辑回归从零实现

深度学习模型训练与逻辑回归从零实现

模型训练

我们从构建一个简单的三层神经网络开始,每层包含五个神经元。以下是构建网络的代码:

import tensorflow as tf
import numpy as np

tf.reset_default_graph()
n1 = 5 # Number of neurons in layer 1
n2 = 5 # Number of neurons in layer 2
n3 = 5 # Number of neurons in layer 3
nx = number_of_x_points
n_dim = nx
n4 = 1
stddev_f = 2.0
tf.set_random_seed(5)
X = tf.placeholder(tf.float32, [n_dim, None])
Y = tf.placeholder(tf.float32, [10, None])
W1 = tf.Variable(tf.random_normal([n1, n_dim], stddev=stddev_f))
b1 = tf.Variable(tf.constant(0.0, shape = [n1,1]) )
W2 = tf.Variable(tf.random_normal([n2, n1], stddev=stddev_f))
b2 = tf.Variable(tf.constant(0.0, shape = [n2,1]))
W3 = tf.Variable(tf.random_normal([n3,n2], stddev = stddev_f))
b3 = tf.Variable(tf.constant(0.0, shape = [n3,1]))
W4 = tf.Variable(tf.random_normal([n4,n3], stddev = stddev_f))
b4 = tf.Variable(tf.constant(0.0, shape = [n4,1]))
X = tf.placeholder(tf.float32, [nx, None]) # Inputs
Y = tf.placeholder(tf.float32, [1, None]) # Labels
# Let's build our network
Z1 = tf.nn.sigmoid(tf.matmul(W1, X) + b1) # n1 x n_dim * n_dim x n_obs = n1 x n_obs
Z2 = tf.nn.sigmoid(tf.matmul(W2, Z1) + b2) # n2 x n1 * n1 * n_obs = n2 x n_obs
Z3 = tf.nn.sigmoid(tf.matmul(W3, Z2) + b3)
Z4 = tf.matmul(W4, Z3) + b4
y_ = Z2

最初,选择了恒等激活函数的神经元作为网络输出 y_ = Z2 ,但训练效果不佳且不稳定。由于需要预测百分比,即输出在 0 到 100 之间,于是尝试使用将 sigmoid 激活函数乘以 100 的方法:

y_ = tf.sigmoid(Z2)*100.0

这样训练效果变得很好。使用 Adam 优化器进行训练,以下是完整的训练代码:

cost = tf.reduce_mean(tf.square(y_-Y))
learning_rate = 1e-3
training_step = tf.train.AdamOptimizer(learning_rate).minimize(cost)
init = tf.global_variables_initializer()
batch_size = 100
sess = tf.Session()
sess.run(init)
training_epochs = 25000
cost_history = np.empty(shape=[1], dtype = float)
train_x = np.transpose(data)
train_y = np.transpose(targets)
cost_history = []
for epoch in range(training_epochs+1):
    for i in range(0, train_x.shape[0], batch_size):
        x_batch = train_x[i:i + batch_size,:]
        y_batch = train_y[i:i + batch_size,:]
        sess.run(training_step, feed_dict = {X: x_batch, Y: y_batch})
    cost_ = sess.run(cost, feed_dict={ X:train_x, Y: train_y})
    cost_history = np.append(cost_history, cost_)
    if (epoch % 1000 == 0):
        print("Reached epoch",epoch,"cost J =", cost_)

训练过程中,成本函数会出现振荡,主要有两个原因:
1. 使用了小批量(mini - batches)训练,导致成本函数振荡。
2. 实验数据存在噪声,因为测量设备并不完美。例如,气体混合器的绝对误差约为 1 - 2%。

以下是训练流程的 mermaid 流程图:

graph LR
    A[初始化网络参数] --> B[定义成本函数和优化器]
    B --> C[初始化会话和变量]
    C --> D[开始训练循环]
    D --> E[获取小批量数据]
    E --> F[运行训练步骤]
    F --> G[计算成本]
    G --> H{是否达到指定轮数}
    H -- 否 --> E
    H -- 是 --> I[结束训练]
逻辑回归从零实现

接下来,尝试从零开始实现逻辑回归模型,不使用 TensorFlow,仅使用 numpy。

数学原理

预测变量 $\hat{y}$ 只能取 0 或 1,它表示在输入 $x$ 的情况下 $\hat{y}$ 为 1 的概率,即 $\hat{y} = P(y = 1|x)$。对于输入观测值,如果 $\hat{y} \geq 0.5$,则定义为类别 1;如果 $\hat{y} < 0.5$,则定义为类别 0。

神经元输出 $\hat{y}$ 对于观测值 $i$ 可以表示为:
$\hat{y} {[i]} = \sigma(z {[i]}) = \sigma(b + w_1x_{1[i]} + w_2x_{2[i]} + \cdots + w_{nx}x_{nx[i]})$

为了找到最佳的权重和偏置,需要最小化成本函数,对于单个观测值,成本函数为:
$\mathcal{L}(\hat{y} {[i]}, y {[i]}) = -y_{[i]}\log(\hat{y} {[i]}) - (1 - y {[i]})\log(1 - \hat{y}_{[i]})$

使用梯度下降算法更新权重和偏置,在第 $n + 1$ 次迭代时,权重和偏置的更新公式如下:
$w_{j[n + 1]} = w_{j[n]} - \gamma\frac{\partial\mathcal{L}(\hat{y} {[i]}, y {[i]})}{\partial w_{j}}$
$b_{[n + 1]} = b_{[n]} - \gamma\frac{\partial\mathcal{L}(\hat{y} {[i]}, y {[i]})}{\partial b}$

对于多个观测值,成本函数 $J(w, b)$ 定义为:
$J(w, b) = \frac{1}{m}\sum_{i = 1}^{m}\mathcal{L}(\hat{y} {[i]}, y {[i]})$

在矩阵形式下,有:
$Z = W^TX + B$
$\hat{Y} = \sigma(Z)$

偏导数的矩阵形式为:
$\frac{\partial J(w, b)}{\partial w_j} = \frac{1}{m}\sum_{i = 1}^{m}(\hat{y} {[i]} - y {[i]})x_{j[i]}$
$\frac{\partial J(w, b)}{\partial b} = \frac{1}{m}\sum_{i = 1}^{m}(\hat{y} {[i]} - y {[i]})$

Python 实现

以下是实现逻辑回归模型的 Python 代码:

import numpy as np
import matplotlib.pyplot as plt

def sigmoid(z):
    s = 1.0 / (1.0 + np.exp(-z))
    return s

def initialize(dim):
    w = np.zeros((dim,1))
    b = 0
    return w,b

def derivatives_calculation(w, b, X, Y):
    m = X.shape[1]
    z = np.dot(w.T,X)+b
    y_ = sigmoid(z)
    cost = -1.0/m*np.sum(Y*np.log(y_)+(1.0-Y)*np.log(1.0-y_))
    dw = 1.0/m*np.dot(X, (y_-Y).T)
    db = 1.0/m*np.sum(y_-Y)
    derivatives = {"dw": dw, "db":db}
    return derivatives, cost

def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):
    costs = []
    for i in range(num_iterations):
        derivatives, cost = derivatives_calculation(w, b, X, Y)
        dw = derivatives ["dw"]
        db = derivatives ["db"]
        w = w - learning_rate*dw
        b = b - learning_rate*db
        if i % 100 == 0:
            costs.append(cost)
        if print_cost and i % 100 == 0:
            print ("Cost (iteration %i) = %f" %(i, cost))
    derivatives = {"dw": dw, "db": db}
    params = {"w": w, "b": b}
    return params, derivatives, costs

def predict (w, b, X):
    m = X.shape[1]
    Y_prediction = np.zeros((1,m))
    w = w.reshape(X.shape[0],1)
    A = sigmoid (np.dot(w.T, X)+b)
    for i in range(A.shape[1]):
        if (A[:,i] > 0.5):
            Y_prediction[:, i] = 1
        elif (A[:,i] <= 0.5):
            Y_prediction[:, i] = 0
    return Y_prediction

def model (X_train, Y_train, X_test, Y_test, num_iterations = 1000, learning_rate = 0.5, print_cost = False):
    w, b = initialize(X_train.shape[0])
    parameters, derivatives, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)
    w = parameters["w"]
    b = parameters["b"]
    Y_prediction_test = predict (w, b, X_test)
    Y_prediction_train = predict (w, b, X_train)
    train_accuracy = 100.0 - np.mean(np.abs(Y_prediction_train - Y_train)*100.0)
    test_accuracy = 100.0 - np.mean(np.abs(Y_prediction_test - Y_test)*100.0)
    d = {"costs": costs, "Y_prediction_test": Y_prediction_test, "Y_prediction_train": Y_prediction_train, "w": w, "b": b, "learning_rate": learning_rate, "num_iterations": num_iterations}
    print ("Accuracy Test: ", test_accuracy)
    print ("Accuracy Train: ", train_accuracy)
    return d

以下是逻辑回归模型实现步骤的表格:
|步骤|描述|
|----|----|
|1|定义 sigmoid 函数|
|2|初始化权重和偏置|
|3|计算导数和成本|
|4|优化权重和偏置|
|5|进行预测|
|6|构建完整模型|

深度学习模型训练与逻辑回归从零实现

模型测试

构建好逻辑回归模型后,需要用数据来测试其效果。这里将使用之前提到的 MNIST 数据集中的数字 1 和 2 的子集进行训练和测试。

数据集准备

使用的优化指标是准确率,为了查看模型能达到的准确率,将使用与之前相同的数据集,即 MNIST 数据集中由数字 1 和 2 组成的子集。以下是获取数据的代码:

from sklearn.datasets import fetch_mldata
mnist = fetch_mldata('MNIST original')
X,y = mnist["data"], mnist["target"]
X_12 = X[np.any([y == 1,y == 2], axis = 0)]
y_12 = y[np.any([y == 1,y == 2], axis = 0)]

由于是一次性加载所有图像,需要创建训练集和开发集(比例为 80% 训练集,20% 开发集),具体操作如下:

shuffle_index = np.random.permutation(X_12.shape[0])
X_12_shuffled, y_12_shuffled = X_12[shuffle_index], y_12[shuffle_index]
train_proportion = 0.8
train_dev_cut = int(len(X_12)*train_proportion)
X_train, X_dev, y_train, y_dev = \
    X_12_shuffled[:train_dev_cut], \
    X_12_shuffled[train_dev_cut:], \
    y_12_shuffled[:train_dev_cut], \
    y_12_shuffled[train_dev_cut:]

接着,对输入进行归一化处理,将矩阵调整为正确的格式,并定义一些常量:

X_train_normalised = X_train/255.0
X_dev_normalised = X_dev/255.0
X_train_tr = X_train_normalised.transpose()
y_train_tr = y_train.reshape(1,y_train.shape[0])
X_dev_tr = X_dev_normalised.transpose()
y_dev_tr = y_dev.reshape(1,y_dev.shape[0])
dim_train = X_train_tr.shape[1]
dim_dev = X_dev_tr.shape[1]

最后,将标签进行转换,从 1 和 2 转换为 0 和 1:

y_train_shifted = y_train_tr - 1
y_test_shifted = y_dev_tr - 1

以下是数据集准备步骤的列表:
1. 加载 MNIST 数据集并筛选出数字 1 和 2 的数据。
2. 打乱数据顺序并划分训练集和开发集。
3. 对输入数据进行归一化处理。
4. 调整矩阵格式。
5. 转换标签。

模型训练与测试

使用准备好的数据集对模型进行训练和测试,调用之前定义的 model 函数:

d = model(X_train_tr, y_train_shifted, X_dev_tr, y_test_shifted, num_iterations = 1000, learning_rate = 0.5, print_cost = True)

以下是模型训练与测试流程的 mermaid 流程图:

graph LR
    A[准备数据集] --> B[调用 model 函数]
    B --> C[初始化权重和偏置]
    C --> D[优化权重和偏置]
    D --> E[进行预测]
    E --> F[计算准确率]
    F --> G[输出结果]
总结

通过以上步骤,完成了一个简单的三层神经网络的训练,以及逻辑回归模型的从零实现和测试。在神经网络训练中,选择合适的激活函数对训练效果有很大影响,使用小批量训练时成本函数会出现振荡,同时实验数据的噪声也会影响训练结果。而从零实现逻辑回归模型,能更深入地理解逻辑回归背后的数学原理和计算过程,体会到像 TensorFlow 这样的深度学习库在背后所做的大量工作。

在实际应用中,可以根据具体需求调整模型的参数,如迭代次数、学习率等,以获得更好的性能。同时,对于不同的数据集,也需要进行适当的预处理,以提高模型的准确率。

以下是整个流程的要点总结表格:
|流程|要点|
|----|----|
|神经网络训练|选择合适激活函数,处理小批量训练振荡和数据噪声问题|
|逻辑回归实现|理解数学原理,用 numpy 实现各步骤|
|数据集准备|加载、划分、归一化和转换标签|
|模型测试|调用模型函数,计算准确率|

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值