Tensorflow2学习--007(卷积神经网络 CNN)

卷积神经网络

卷积神经网络(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。

卷积神经网络由一个或多个卷积层和顶端的全连通层(对应经典的神经网络)组成,同时也包括关联权重和池化层(pooling layer)。
这一结构使得卷积神经网络能够利用输入数据的二维结构。与其他深度学习结构相比,卷积神经网络在图像和语音识别方面能够给出更好的结果。
这一模型也可以使用反向传播算法进行训练。相比较其他深度、前馈神经网络,卷积神经网络需要考量的参数更少,使之成为一种颇具吸引力的深度学习结构。

---------> https://zh.m.wikipedia.org/zh-hans/卷积神经网络

1. 卷积神经网络 相对于全连接层 模拟生物 每次卷积 仅仅关注 一张图形的一个部分 提前一部分的特征
   对于同一个卷积核 每次卷积核的移动 相当于 在相同的角度 观察一张图片的不同部分 将每一块的特征
   进行了提取抽象 
   而全连接层可以理解为 每个节点 都是一个角度 并且是在该角度观察整体

2. 同一个卷积核的参数在滑动窗口时 共享 所以卷积核的参数相对于 全连接层有了质的减少

image

image

运用卷积核来对特征的提取

image

可以看出 卷积网络在对图片特征进行一层层的提取

卷积层(线性求和)

创建卷积层
layers.Conv2D(N,kernel_size=,strides=,padding=)
一个卷积核的个数 会根据输入的Channel自动适应 
N: 输出的Channel数量(卷积核的个数)
kernel_size: 卷积核的尺寸
strides: 步长
padding: 边框    'valid' 不padding  
                'same' 会使输出和输入的size保存一致(在strides为1的情况下)

卷积核存储形式: [w,h,c,N] c:输入通道数 N:输出通道数  bias:[N]

仅卷积运算(不会对卷积核进行管理 仅仅进行卷积运算)
out = tf.nn.conv2d(x,w,strides=,padding=)
out = out + b
x: 输入
w: 卷积核
b: 偏置

下采样层 pooling层(求均或者求最大)

image

对于pooling stride影响输出大小       
pooling层的通道数不变 仅改变size

pool = layers.MaxPool2D(k_size,strides=)
size表示核的尺寸
核的个数会自动的匹配输入的channel

仅pooling运算 
out = tf.nn.max_pool2d(x,2,strides=,padding=)

上采样层 upsample层

image

layer = layers.UpSampling2D(size=)
size: 方法倍数  

ReLU层(卷积后 并没有进入一个激活函数进行激活 而是进入激活层来激活)

image

其实就是激活函数

tf.nn.relu(x)
对输入的值进行激活

layer = layers.ReLU()
创建激活层 不需要任何参数

实现VGG13

点击查看代码
import tensorflow
import tensorflow as tf
import tensorflow.keras as keras

conv_layers = [
    # unit 1
    # 后面的2D 代表了 在2D的纬度上面
    keras.layers.Conv2D(64, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.Conv2D(64, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    # 输出 [b,w/2,h/2,64]

    # unit 2
    keras.layers.Conv2D(128, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.Conv2D(128, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    # 输出 [b,w/4,h/4,128]
    # unit 3
    keras.layers.Conv2D(256, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.Conv2D(256, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    # 输出 [b,w/8,h/8,256]
    # unit 4
    keras.layers.Conv2D(512, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.Conv2D(512, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    # unit 5
    keras.layers.Conv2D(512, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.Conv2D(512, [3, 3], padding='same', activation=tf.nn.relu),
    keras.layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    # 输出 [b,w/32,h/32,512]
]

# 全连接层网络
fc_net = [
    keras.layers.Dense(256, activation=tf.nn.relu),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(100, activation=None)
]


def processor(x, y):
    # 归一化
    x = tf.cast(x, dtype=tf.float32) / 255.
    y = tf.cast(y, dtype=tf.int32)
    return x, y


batchs = 128

(x, y), (x_test, y_test) = keras.datasets.cifar100.load_data()

# squeeze 纬度挤压 下面的操作 可以将 为1的纬度给挤压掉
y = tf.squeeze(y, axis=1)
y_test = tf.squeeze(y_test, axis=1)

db = tf.data.Dataset.from_tensor_slices((x, y))
db = db.map(processor).shuffle(2000)
db = db.batch(batchs)
db_test = tf.data.Dataset.from_tensor_slices((x_test, y_test))
db_test = db_test.map(processor).shuffle(2000)
db_test = db_test.batch(batchs)

sample = next(iter(db_test))
print(sample[0].shape, sample[1].shape)

if __name__ == "__main__":
    conv_net = keras.Sequential(conv_layers)
    fc_net = keras.Sequential(fc_net)
    conv_net.build(input_shape=[None, 32, 32, 3])
    fc_net.build(input_shape=[None, 512])

    variables = conv_net.trainable_variables + fc_net.trainable_variables
    optimizer = keras.optimizers.Adam(lr=1e-4)
    # x = tf.random.normal([4,32,32,3])
    # out = conv_net(x)
    # print(out.shape)
    for epoch in range(50):
        for step, (x, y) in enumerate(db):
            with tf.GradientTape() as tape:
                out = conv_net(x)
                out = tf.reshape(out, [-1, 512])
                logits = fc_net(out)
                y_onehot = tf.one_hot(y, depth=100)
                loss = tf.losses.categorical_crossentropy(y_onehot, logits, from_logits=True)
                loss = tf.reduce_mean(loss)

            # 将loss 对 参数进行求导
            grad = tape.gradient(loss, variables)
            optimizer.apply_gradients(zip(grad, variables))

            if step % 100 == 0:
                print(epoch, step, "loss:", loss)

        total = 0
        total_correct = 0
        for x, y in db_test:
            out = conv_net(x)
            out = tf.reshape(out, [-1, 512])
            logits = fc_net(out)  # [b,10]
            prob = tf.nn.softmax(logits, axis=1)
            pred = tf.argmax(prob, axis=1)
            pred = tf.cast(pred, dtype=tf.int32)
            correct = tf.cast(tf.equal(pred, y), tf.int32)
            # 正确的个数
            correct = tf.reduce_sum(correct)
            # 总个数
            total += y.shape[0]
            total_correct += correct
        acc = total_correct / total
        print("acc: ",acc)

# 除了使用这种方法 实现 其实还可以使用 自定义model的方法来 便捷的训练网络
# 使用这种方法的话 就不可以便捷的训练网络 因为 有个 reshape 操作
# 不能直接使用模型 需要对两个模型进行拼接 还要使用 梯度来进行更新
posted @ 2022-10-26 20:28  cc学习之路  阅读(168)  评论(0)    收藏  举报