«

初识Keras: 轻松完成神经网络模型搭建

时间:2023-2-27 23:31     作者:wen     分类: AI


一路走来, 我们已经到了第八节课, 前面的七节课我们在研究关于神经网络底层基本原理, 那么这节课就是我们的分水岭, 以后我们就不再进行底层的数学原理分析, 对于工程实践而言, 目前我们所学的内容已经可以开始了, 但是如果你决定踏入机器学习神经网络的大门, 并准备衣带渐宽终不悔的深入这个领域, 那么希望你自己继续研究其背后的数学原理, 和该领域目前新的想法.

在此之前, 我们来聊一聊, 最后一个稍微麻烦一点的问题. 当然或许这并是不一个问题, 而是将一直陪伴我们接下来课程的工具. 我们在上一课中说到, 随着输入数据的特征越来越多, 如果一个个的去编写函数表达式, 未免有点麻烦和拖沓, 所以我们需要使用一个数学工具, 来让这件事情变得简单. 矩阵和向量, 向量和矩阵到底是什么? 很多人尝试从不同的角度去解释问题, 物理学家说是空间变换, 数据工程师说是数据的特征, 程序员们会说是数组(多维数组). 办公人士看见多半会说, 唉这不就是Excel表格的行和列吗? 不同领域的人对它的理解各不相同, 一旦我们偏向于某个角色去认识它, 就会因为其他角色的声音让我们困惑.

我们更需要用数学家的眼光来看待它, 矩阵和向量他就是一个工具, 正如数学是服务其他科学的工具一样, 他是数学的工具, 我们最开始学数学的时候, 接触的是最简单的一元一次函数. 后来我们又接触二元一次函数, 如果你对初中数学还有印象. 那么鸡兔同笼的问题就在这里, 后来, 我们又接触到三元, 四元, 五元等等. 我们发现, 原来这个元可以无限的多下去. 这时候向来有"洁癖"的数学家跳出来了, 说到: 这不够优雅也不方便, 不然我们发明一个工具吧! 于是向量出现了, 我们用三元一次函数举例:

用一个向量把这个函数的自变量放入其中, 在把权重系数放入一个向量中, 把偏置系数b也放入一个向量中, 基本的表示方法有了, 那就规定一些运算规则吧! 我们给向量定一个"转置" 的规则, 在向量上写个T表示把对它进行转置, 转置后向量也就从一行竖成一列, 转置的操作可以简单的理解为, 把躺着的一行支棱起来变成一列, 在规定一下向量之间的乘法吧! 向量X点乘WT转置,咋们这样算, 横和竖对象的元素相乘再加起来, X·WT = [w1x1+w2x2+w3x3], 当然这是向量和向量的运算, 所以这个运算的结果也应该是个向量. 这样才统一, 虽然这个向量只有一个元素, 但如果我们用"万物皆向量"的世界观去看待问题, 把单个数字看作一个一维向量也就不奇怪了, 最后我们让计算的结果再加上偏置项向量B, 根据向量的加法, 最后得到的结果是这样的: X·WT = [w1x1+w2x2+w3x3]+[b] = [w1x1+w2x2+w3x3 +b], 这个计算结果恰好就是我们函数的运算结果. 你看 Z = X·WT +B 就可以用向量的运算去表示线性函数了. X是输入向量, W是权重系数向量, B是偏置项向量, Z是结果向量, 这不比上面简洁许多吗? 当然三个输入或许你还感受不到这种简洁到底有多棒, 那如果是一个1000元一次函数呢? 如果不用向量的手段, 那我们只有用省略号, 否则一页纸估计是写不完的. 但如果我们使用向量运算, 那么这个1000元一次线性函数让旧是: Z = X·WT +B , 只不过这时候我们需要清楚X和W的元素数量都是1000, 有时候向量的元素数量也叫维度, 这一点大家在中学时代初学向量的时候应该很清楚.

我们用一个二维向量表示二维平面上的一个矢量, 用三维向量表示三维空间的一个矢量, 当然我们也可以用一个四维向量表示四维空间的矢量, 只不过作为三维生物, 我们已经无法直观感受它的样子, 所以不再具备物理意义而成为了纯粹的数学手段, 但不管这个线性函数是多少元, 都可以采用这种向量运算的方式表示这个函数, 输入是向量, 权重参数和偏置项参数是向量, 计算结果输出也是向量. 那从这种角度去考虑我们最开始的单神经元, w, x, b 是一个一维向量, z也是个一维向量, 对于两个维度输入数据的模型, x, w, 都是一个二维向量, 我们不需要写繁琐的式子了, 这让事情变的很简洁, 很统一. 但追求极致的数学家们又发现, 如果输入数据是一组函数进行运算, 比如我们加入两个隐藏层神经元的神经网络, 那输入实际上就是分别送入两个线性函数进行计算, 这时候单靠向量似乎也不是很酸爽了, 每一个函数都有一个权重系数向量和偏置系数向量, 那为什么不把它们放在一起呢?

于是矩阵出现, 同样这个矩阵也有转置的操作, 其实你大可以把矩阵看作由多个向量并在一起形成的"特殊向量", 所以矩阵的转置就可以简单的看作, 对其中的每一个向量进行转置操作, 第一行竖起来变成第一列, 第二行竖起来变成第二列, 转置以后, 在让输入向量X点乘这个系数矩阵, 而输入向量X和这个权重系数矩阵的点乘运算也可以看作, 分别让输入向量X点乘第一列算出一个计算结果, 再让输入向量X点乘第二列计算出第二个结果, 这两个计算结果组成一个结果向量, 在让这个结果向量加上偏置项向量b, 最终的结果向量中的两个元素, 分别表示这两个函数的运算结果, 所以, 你会发现此时, 对于多个函数, 我们任然可以用 Z = X·WT +B表示. 只不过我们想在要明确W是一个2*3的矩阵, 没错, 矩阵和向量就是能把事情做的如此的简单和统一.

不论输入的数据有多少维度, 也不管输入数据送入了多少个神经元做线性函数运算, 数学形式都统一成一样的了, 如此大事成矣! 数学家爽了, 那程序元能不能也爽呢? 当然可以, 像python中的numpy这种数学库, 在做矩阵和向量计算的时候就和这里的数学符号一样, 非常的简洁方便, 那我们就在编程实验中一探究竟吧!

# dataset.py
# 预测数据
import numpy as np

def get_beans(counts):
    xs = np.random.rand(counts, 2) * 2
    ys = np.zeros(counts)
    for i in range(counts):
        x = xs[i]
        if (x[0] - 0.5 * x[1] - 0.1) > 0:
            ys[i] = 1
    return xs, ys

def get_beans1(counts):
    xs = np.random.rand(counts)
    xs = np.sort(xs)
    ys = np.zeros(counts)
    for i in range(counts):
        x = xs[i]
        yi = 0.7 * x + (0.5 - np.random.rand()) / 50 + 0.5
        if yi > 0.8:
            ys[i] = 1
        else:
            ys[i] = 0
    return xs, ys

def get_beans2(counts):
    xs = np.random.rand(counts) * 2
    xs = np.sort(xs)
    ys = np.zeros(counts)
    for i in range(counts):
        x = xs[i]
        yi = 0.7 * x + (0.5 - np.random.rand()) / 50 + 0.5
        if yi > 0.8 and yi < 1.4:
            ys[i] = 1

    return xs, ys

def get_beans3(counts):
    xs = np.random.rand(counts) * 2
    xs = np.sort(xs)
    ys = np.zeros(counts)
    for i in range(counts):
        x = xs[i]
        yi = 0.7 * x + (0.5 - np.random.rand()) / 50 + 0.5
        if yi > 0.8 and yi < 1.4:
            ys[i] = 1

        if yi > 1.6 and yi < 1.8:
            ys[i] = 1
    return xs, ys

def get_beans4(counts):
    xs = np.random.rand(counts, 2) * 2
    ys = np.zeros(counts)
    for i in range(counts):
        x = xs[i]
        if (np.power(x[0] - 1, 2) + np.power(x[1] - 0.3, 2)) < 0.5:
            ys[i] = 1

    return xs, ys
# plot_utils.py
# 绘图工具
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

from keras.models import Sequential

def show_scatter_curve(X, Y, pres):
    plt.scatter(X, Y)
    plt.plot(X, pres)
    plt.show()

def show_scatter(X, Y):
    if X.ndim > 1:
        show_3d_scatter(X, Y)
    else:
        plt.scatter(X, Y)
        plt.show()

def show_3d_scatter(X, Y):
    x = X[:, 0]
    z = X[:, 1]
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(x, z, Y)
    plt.show()

def show_surface(x, z, forward_propgation):
    x = np.arange(np.min(x), np.max(x), 0.1)
    z = np.arange(np.min(z), np.max(z), 0.1)
    x, z = np.meshgrid(x, z)
    y = forward_propgation(x, z)
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.plot_surface(x, z, y, cmap='rainbow')
    plt.show()

def show_scatter_surface(X, Y, forward_propgation):
    if type(forward_propgation) == Sequential:
        show_scatter_surface_with_model(X, Y, forward_propgation)
        return
    x = X[:, 0]
    z = X[:, 1]
    y = Y

    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(x, z, y)

    x = np.arange(np.min(x), np.max(x), 0.1)
    z = np.arange(np.min(z), np.max(z), 0.1)
    x, z = np.meshgrid(x, z)

    X = np.column_stack((x[0], z[0]))
    for j in range(z.shape[0]):
        if j == 0:
            continue
        X = np.vstack((X, np.column_stack((x[0], z[j]))))

    r = forward_propgation(X)
    y = r[0]
    if type(r) == np.ndarray:
        y = r

    y = np.array([y])
    y = y.reshape(x.shape[0], z.shape[1])
    ax.plot_surface(x, z, y, cmap='rainbow')
    plt.show()

def show_scatter_surface_with_model(X, Y, model):
    # model.predict(X)

    x = X[:, 0]
    z = X[:, 1]
    y = Y

    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(x, z, y)

    x = np.arange(np.min(x), np.max(x), 0.1)
    z = np.arange(np.min(z), np.max(z), 0.1)
    x, z = np.meshgrid(x, z)

    X = np.column_stack((x[0], z[0]))

    for j in range(z.shape[0]):
        if j == 0:
            continue
        X = np.vstack((X, np.column_stack((x[0], z[j]))))

    y = model.predict(X)

    # return
    # y = model.predcit(X)
    y = np.array([y])
    y = y.reshape(x.shape[0], z.shape[1])
    ax.plot_surface(x, z, y, cmap='rainbow')
    plt.show()

def pre(X, Y, model):
    model.predict(X)
# vec_caculate.py
import numpy as np
import dataset
import plot_utils

m = 100

X, Y = dataset.get_beans(m)
print(X, Y)

# 绘制散点图
plot_utils.show_scatter(X, Y)

# w1 = 0.1
# w2 = 0.1
W = np.array([0.1, 0.1])
# b = 0.1
B = np.array([0.1])

# 前向传播
def forward_propgation(X):
    # z = w1 * x1s + w2 * x2s + b
    Z = X.dot(W.T) + B
    # a = 1 / (1 + np.exp(-z))
    A = 1 / (1 + np.exp(-Z))
    return A

plot_utils.show_scatter_surface(X, Y, forward_propgation)

for _ in range(500):
    for i in range(m):
        Xi = X[i]
        Yi = Y[i]
        # 前向传播
        A = forward_propgation(Xi)
        # 代价函数
        E = (Yi - A) ** 2
        # 反向传播
        dEdA = -2 * (Yi - A)
        dAdZ = A * (1 - A)
        dZdW1 = Xi
        dZdB = 1
        dEdW = dEdA * dAdZ * dZdW1
        dEdB = dEdA * dAdZ * dZdB
        # 学习率
        alpha = 0.01
        # 梯度下降
        W = W - alpha * dEdW
        B = B - alpha * dEdB

plot_utils.show_scatter_surface(X, Y, forward_propgation)
# vec_one_hidden_layer.py
import dataset
import plot_utils

import numpy as np

# 从数据中获取随机豆豆
m = 100
X, Y = dataset.get_beans4(m)
print(X)
print(Y)

plot_utils.show_scatter(X, Y)

W1 = np.random.rand(2, 2)
B1 = np.random.rand(1, 2)
W2 = np.random.rand(1, 2)
B2 = np.random.rand(1, 1)

def forward_propgation(X):
    # Z1:(m,2)
    Z1 = X.dot(W1.T) + B1

    # A1:(m,2)
    A1 = 1 / (1 + np.exp(-Z1))
    # Z2:(m,1)
    Z2 = A1.dot(W2.T) + B2
    # A2:(m,1)
    A2 = 1 / (1 + np.exp(-Z2))
    return A2, Z2, A1, Z1

plot_utils.show_scatter_surface(X, Y, forward_propgation)

for _ in range(5000):
    for i in range(m):
        Xi = X[i]

        Yi = Y[i]
        A2, Z2, A1, Z1 = forward_propgation(Xi)

        E = (Yi - A2) ** 2

        # (1,1)
        dEdA2 = -2 * (Yi - A2)
        # (1,1)
        dEdZ2 = dEdA2 * A2 * (1 - A2)

        # (1,2)
        dEdW2 = dEdZ2 * A1
        # (1,1)
        dEdB2 = dEdZ2 * 1

        # (1,2)
        dEdA1 = dEdZ2 * W2

        # (1,2)
        dEdZ1 = dEdA1 * A1 * (1 - A1)

        dEdW1 = (dEdZ1.T).dot(np.array([Xi]))

        dEdB1 = dEdZ1 * 1

        alpha = 0.05
        W2 = W2 - alpha * dEdW2
        B2 = B2 - alpha * dEdB2

        W1 = W1 - alpha * dEdW1

        B1 = B1 - alpha * dEdB1
    # 计算准确率

    A2, Z2, A1, Z1 = forward_propgation(X)
    A2 = np.around(A2)  # 四舍五入取出0.5分割线左右的分类结果
    A2 = A2.reshape(1, m)[0]
    accuracy = np.mean(np.equal(A2, Y))
    print("准确率:" + str(accuracy))

plot_utils.show_scatter_surface(X, Y, forward_propgation)
# keras_for_one.py
import dataset
import numpy as np
import plot_utils
from keras.models import Sequential
from keras.layers import Dense

m = 100
X, Y = dataset.get_beans1(m)

plot_utils.show_scatter(X, Y)

model = Sequential()
# units 当前层神经元数量
# activation 激活函数类型
# input_dim 输入数据特征维度
model.add(Dense(units=1, activation='sigmoid', input_dim=1))
# loss 代价函数 mean_squared_error (均方误差)
# optimizer 优化器 sgd(随机梯度下降算法)
# metrics 评估标准 accuracy(准确度)
model.compile(loss='mean_squared_error', optimizer='sgd', metrics=['accuracy'])

# epochs 训练回合数
# batch_size 训练批数量
model.fit(X, Y, epochs=6000, batch_size=10)

pres = model.predict(X)

plot_utils.show_scatter_curve(X, Y, pres)
# keras_for_tow.py
import dataset
import numpy as np
import plot_utils
from keras.models import Sequential
from keras.layers import Dense
from tensorflow.keras.optimizers import SGD

m = 100
X, Y = dataset.get_beans2(m)

plot_utils.show_scatter(X, Y)

model = Sequential()
# units 当前层神经元数量
# activation 激活函数类型
# input_dim 输入数据特征维度
model.add(Dense(units=2, activation='sigmoid', input_dim=1))
model.add(Dense(units=1, activation='sigmoid'))
# loss 代价函数 mean_squared_error (均方误差)
# optimizer 优化器 sgd(随机梯度下降算法)
# metrics 评估标准 accuracy(准确度)
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.05), metrics=['accuracy'])

# epochs 训练回合数
# batch_size 训练批数量
model.fit(X, Y, epochs=5000, batch_size=10)

pres = model.predict(X)

plot_utils.show_scatter_curve(X, Y, pres)
# keras_for_blue.py
# keras框架运用
import dataset
import numpy as np
import plot_utils
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import SGD

m = 100
# X, Y = dataset.get_beans(m)
X, Y = dataset.get_beans4(m)

plot_utils.show_scatter(X, Y)

model = Sequential()
# units 当前层神经元数量
# activation 激活函数类型
# input_dim 输入数据特征维度
model.add(Dense(units=2, activation='sigmoid', input_dim=2))
model.add(Dense(units=1, activation='sigmoid'))

# loss 代价函数 mean_squared_error (均方误差)
# optimizer 优化器 sgd(随机梯度下降算法)
# metrics 评估标准 accuracy(准确度)
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.05), metrics=['accuracy'])

# epochs 训练回合数
# batch_size 训练批数量
model.fit(X, Y, epochs=5000, batch_size=10)

pres = model.predict(X)
# 曲线图
# plot_utils.show_scatter_curve(X,Y,pres)
# 曲面图
plot_utils.show_scatter_surface(X, Y, model)

标签: 人工智能