深度学习-卷积神经网络-tf-DNN-50

1. my_nn

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
"""
import os
import gzip
import numpy as np
from sklearn.datasets import fetch_mldata
from sklearn.utils.extmath import safe_sparse_dot


def load_data(data_folder):
    files = [
        'train-labels-idx1-ubyte.gz', 'train-images-idx3-ubyte.gz',
        't10k-labels-idx1-ubyte.gz', 't10k-images-idx3-ubyte.gz'
    ]

    paths = []
    for fname in files:
        paths.append(os.path.join(data_folder, fname))

    with gzip.open(paths[0], 'rb') as lbpath:
        y_train = np.frombuffer(lbpath.read(), np.uint8, offset=8)

    with gzip.open(paths[1], 'rb') as imgpath:
        x_train = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(
            len(y_train), -1)

    with gzip.open(paths[2], 'rb') as lbpath:
        y_test = np.frombuffer(lbpath.read(), np.uint8, offset=8)

    with gzip.open(paths[3], 'rb') as imgpath:
        x_test = np.frombuffer(
            imgpath.read(), np.uint8, offset=16).reshape(len(y_test), 28, 28)

    return (x_train, y_train), (x_test, y_test)


def tran_y(y_true):
    y_one = np.zeros(10)
    y_one[int(y_true)] = 1
    return y_one


(X, y), (test_images, test_labels) = load_data('data/')

print(X.shape)
print(y.shape)

y = np.array([tran_y(y[i]) for i in range(len(y))])

hidden_layer_size = [300, 100]
max_iter = 20  # todo
alpha = 0.001  # 正则项系数   Loss = bass_loss + alpha*regular_loss
learning_rate = 0.001


def log_loss(y_true, y_prob):
    """
    这是一个计算对数损失(log loss)的函数,输入参数为真实标签(y_true)和预测概率(y_prob)。具体实现步骤如下:

    1. 对预测概率进行裁剪,保证其在[1e-10, 1-1e-10]之间,避免出现取对数时出现无穷大或NaN的情况。
    2. 判断预测概率和真实标签的维度是否为1,若为1则将其转化为二维数组。
    3. 计算对数损失,公式为-sum(y_true * log(y_prob)) / y_prob.shape[0],其中y_true和y_prob均为二维数组,*表示元素间的乘积,/表示元素间的除法。
    :param y_true:
    :param y_prob:
    :return:
    """
    # 对数损失计算
    y_prob = np.clip(y_prob, 1e-10, 1 - 1e-10)
    if y_prob.shape[1] == 1:
        y_prob = np.append(1 - y_prob, y_prob, axis=1)
    if y_true.shape[1] == 1:
        y_true = np.append(1 - y_true, y_true, axis=1)
    return -np.sum(y_true * np.log(y_prob)) / y_prob.shape[0]  # * 表示对应的位置相乘


def softmax(x):
    """
    这是一个实现softmax函数的代码,它可以将一个向量转换为概率分布。softmax函数常用于神经网络中的分类问题。该函数的输入是一个n维向量,输出也是一个n维向量,其中每个元素的值都在0到1之间,且所有元素的和为1。
    具体实现中,首先对输入向量进行归一化,即每个元素减去该向量中的最大值。这一步的目的是防止指数函数的溢出。然后使用指数函数对归一化后的向量进行变换,最后再次进行归一化,得到概率分布。
    :param x:
    :return:
    """
    tmp = x - x.max(axis=1)[:, np.newaxis]
    np.exp(tmp, out=x)
    x /= x.sum(axis=1)[:, np.newaxis]
    return x


def relu(x):
    """
    这是一个Python函数,用于实现ReLU(Rectified Linear Unit)激活函数。它的作用是将输入的值x进行非线性转换,将负值变为0,保留正值。具体实现是通过使用NumPy库的clip函数,将x中小于0的值变为0,大于0的值保持不变。最后返回转换后的结果。
    :param x:
    :return:
    """
    np.clip(x, 0, np.finfo(x.dtype).max, out=x)
    return x


def relu_derivation(z, delta):
    """
    relu的导数
    z为0的改为零 有值的则保持不变
    :param z:
    :param delta:
    :return:
    """
    delta[z == 0] = 0


def gen_batch(n, bs):
    """
    n 样本总数
    bs batch_size 批次大小
    返回一个切片的索引
    :param n:
    :param bs:
    :return:
    """
    stat = 0
    for _ in range(int(n // bs)):
        end = stat + bs
        yield slice(stat, end)

    if stat < n:
        yield slice(stat, n)


# 样本条数6000 输入的特征 28*28=784
n_samples, n_features = X.shape

# 输出节点数10
n_outputs = y.shape[1]

# 批次的大小200
batch_size = min(200, n_samples)

layer_units = ([n_features] + hidden_layer_size + [n_outputs])
print("====>nn 各层的节点数: 输入层 2隐藏层 输出层:", layer_units)
n_layers = len(layer_units)

"""
这段代码看起来像是神经网络中的权重和偏置初始化过程。具体来说,这个神经网络有n_layers层,每一层的神经元数量由layer_units列表中的值决定。这个for循环遍历每一层,计算该层的输入和输出神经元数量(fan_in和fan_out),然后使用一个公式来计算初始权重的范围(init_bound),最后使用均匀分布来随机初始化权重和偏置(coef_init和intercept_init),并将它们存储在coefs_和intercepts_列表中。这个过程可以帮助神经网络更好地学习数据的特征。
"""
coefs_ = []
intercepts_ = []
# 输入层 --> 隐藏层1 --> 隐藏层2 --> 输出层
for i in range(n_layers - 1):
    fan_in = layer_units[i]
    fan_out = layer_units[i + 1]
    factor = 6.

    init_bound = np.sqrt(factor / (fan_in + fan_out))
    coefs_init = np.random.uniform(-init_bound, +init_bound, (fan_in, fan_out))
    coefs_.append(coefs_init)

    intercepts_init = np.random.uniform(-init_bound, +init_bound, fan_out)
    intercepts_.append(intercepts_init)

# 初始化各层的输出 用于存放正向传播的输出结果
# 第一层是感知层
# 这段代码是神经网络中的正向传播过程中的一部分。在神经网络中,每一层都有一些神经元,每个神经元都有一些权重和偏置,用于计算该神经元的输出。正向传播是指从输入层开始,不断将每一层的输出作为下一层的输入,最终得到输出层的输出结果。
# 在这段代码中,首先将输入层的输出X存入一个列表activations中。然后对于每一层,都创建一个空数组,用于存放该层的输出结果。这里的n_fan_out表示该层中神经元的个数,因此创建的空数组的大小为(batch_size, n_fan_out),其中batch_size表示一次正向传播中输入的数据样本数量。
# 最后,将所有层的输出结果存放在一个列表activations中,用于后续的计算。
activations = [X]
activations.extend(
    np.empty((batch_size, n_fan_out)) for n_fan_out in layer_units[1:])

# 反向传播 对w进行调整 调整量delta 与 w的形状是一样的
deltas = [np.empty_like(a_layer) for a_layer in activations]

# 初始化层之间的w矩阵 偏Loss/偏w
coef_grads = [np.empty((n_fan_in, n_fan_out)) for n_fan_in, n_fan_out in
              zip(layer_units[1:], layer_units[:-1])]
# 初始化层之间的b 偏Loss/偏b
intercepts_grads = [np.empty(n_fan_out) for n_fan_out in layer_units[1:]]

loss = 0.

# min batch 梯度下降
for it in range(max_iter):

    arr = np.arange(n_samples)
    np.random.shuffle(arr)
    X = X[arr]
    y = y[arr]

    accumulated_loss = 0.
    for batch_slice in gen_batch(n_samples, batch_size):
        batch_x = X[batch_slice]
        batch_y = y[batch_slice]

        # 输入层赋值
        activations[0] = batch_x

        # 正向传播
        for i in range(n_layers - 1):
            # w*X + b
            activations[i + 1] = safe_sparse_dot(activations[i], coefs_[i])
            activations[i + 1] += intercepts_[i]

            if (i + 1) != (n_layers - 1):  # 如果是隐藏层 需要经过relu激活函数
                activations[i + 1] = relu(activations[i + 1])
        # 最后一层
        activations[i + 1] = softmax(activations[i + 1])

        # loss计算
        loss = log_loss(batch_y, activations[-1])

        # 正则项损失
        l2_loss = np.sum(
            np.array([np.dot(s.ravel(), s.ravel()) for s in coefs_]))
        loss += (0.5 * alpha) * l2_loss / len(batch_y)
        accumulated_loss += loss * len(batch_y)

        # 反向传播 先计算最后一层last layer
        last = n_layers - 2
        deltas[last] = activations[-1] - batch_y  # y_hat - y
        # base loss梯度的计算 X*(y_hat-y)
        coef_grads[last] = safe_sparse_dot(activations[last].T, deltas[last])
        # L2 loss 梯度的计算 1/2*w**2 求导--> w
        coef_grads[last] += (alpha * coefs_[last])
        # 梯度 求平均
        coef_grads[last] /= n_samples
        # 截距项b的梯度 y_hat-y
        intercepts_grads[last] = np.mean(deltas[last], 0)

        # 反向传播
        for i in range(n_layers - 2, 0, -1):
            # delta_prev = delta*W*激活函数的导数
            deltas[i - 1] = safe_sparse_dot(deltas[i], coefs_[i].T)
            relu_derivation(activations[i], deltas[i - 1])

            # 1. base Loss的梯度
            coef_grads[i - 1] = safe_sparse_dot(activations[i - 1].T,
                                                deltas[i - 1])
            # 2. L2 loss的梯度
            coef_grads[i - 1] += (alpha * coefs_[i - 1])
            # 3. 求平均
            coef_grads[i - 1] /= n_samples
            # 4. 截距项的梯度
            intercepts_grads[i - 1] = np.mean(deltas[i - 1], 0)

        # 一次小批次正向传播 反向传播 结束后 跟新 w b
        grads = coef_grads + intercepts_grads
        # 更新量= -学习率*梯度
        updates = [-learning_rate * grad for grad in grads]
        # 跟新
        for param, update in zip(coefs_ + intercepts_, updates):
            param += update

    # 每个epoch结束后 打印一下loss
    loss_ = accumulated_loss / X.shape[0]
    print("Iter:%s, loss=%.8f" % (it, loss_))

2. mnist_cnn

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# 读取数据
mnist = input_data.read_data_sets('MNIST_data_bak/', one_hot=True)


# 截断的正态分布 标准差设置为0.1
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)


# 因为使用的激活函数为relu, 给偏置项增加小的正值0.1 避免节点死亡(dead neurons)
def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)


# conv2d 二维的卷积函数 x数入 W是卷积核 [5, 5, ,1, 32]
# 卷积核的尺寸 5*5 1代表channel mnist是单通道的灰色图片
# 32 代表卷积核 的数量 也就是这个卷积层会提取多少的feature_map
# strides=[1, 1, 1, 1] 当步长为1的时候 输出与输出会保持同样的尺寸
# [n_sample, height, width, channels]
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding="SAME")


# tf.nn.max_pool是tf中的最大池化函数 2*2 降为1*1
# max_pool是保留原始像素中灰度值最大的那个 即保留最显著的特征
# strides [n_sample, height, width, channels]
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],
                          padding="VALID")


# 卷积的计算需要将1D的输入 转化成2D的图片结构  1*784 --> 28*28
# [-1, 28, 28, 1] -1代表数量不固定, 最后的1 代表1个通道
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1, 28, 28, 1])

W_conv1 = weight_variable([5, 5, 1, 32])  # 5*5的核 channel=1 数量32
b_conv1 = bias_variable([32])  # 核的数量32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

# 第个卷积层 与上面的一样
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

# 经过2次的卷积池化 输出为 m*7*7*64
# 将第二层的输出 一张图片变成7*7*64 需要reshape转化成1D的向量
# 然后连接一个全连接层 节点的数量为 1024 并经过relu激活
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

# 防止过拟合 训练的时候使用dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 再添加一个隐藏层 节点数 256
W_fc_plus = weight_variable([1024, 256])
b_fc_plus = bias_variable([256])
h_fc_plus = tf.nn.relu(tf.matmul(h_fc1_drop, W_fc_plus) + b_fc_plus)

# 再接上dropout
h_fc_plus_drop = tf.nn.dropout(h_fc_plus, keep_prob)

# 接softmax分类
W_fc2 = weight_variable([256, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc_plus_drop, W_fc2) + b_fc2)

# 损失函数
cross_entropy = tf.reduce_mean(
    -tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1]))

# 训练步骤
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)  # 学习率0.0001

# 预测准确率的计算
correct_prediction = tf.equal(tf.arg_max(y_conv, 1), tf.arg_max(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

# 训练
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    # 训练迭代次数
    for i in range(2000):
        batch = mnist.train.next_batch(50)

        # 训练的过程中计算下精度
        if i % 100 == 0:
            train_acc = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})
            print("train accuracy: %s" % train_acc)

        train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

    # 训练结束 测试集 验证下精度
    x_test, y_test = mnist.test.next_batch(5000)
    acc = accuracy.eval(feed_dict={x: x_test, y_: y_test, keep_prob: 1.0})
    print("Test dataset acc: %s" % acc)



posted @ 2024-02-29 11:08  jack-chen666  阅读(3)  评论(0编辑  收藏  举报