| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- #%%
- import os
- os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
- import tensorflow as tf
- from tensorflow import keras
- from tensorflow.keras import layers, optimizers, datasets, Sequential
- #%%
- x = tf.random.normal([2,5,5,3]) # 模拟输入,3通道,高宽为5
- # 需要根据[k,k,cin,cout]格式创建,4个卷积核
- w = tf.random.normal([3,3,3,4])
- # 步长为1, padding为0,
- out = tf.nn.conv2d(x,w,strides=1,padding=[[0,0],[0,0],[0,0],[0,0]])
- # %%
- x = tf.random.normal([2,5,5,3]) # 模拟输入,3通道,高宽为5
- # 需要根据[k,k,cin,cout]格式创建,4个卷积核
- w = tf.random.normal([3,3,3,4])
- # 步长为1, padding为1,
- out = tf.nn.conv2d(x,w,strides=1,padding=[[0,0],[1,1],[1,1],[0,0]])
- # %%
- x = tf.random.normal([2,5,5,3]) # 模拟输入,3通道,高宽为5
- w = tf.random.normal([3,3,3,4]) # 4个3x3大小的卷积核
- # 步长为,padding设置为输出、输入同大小
- # 需要注意的是, padding=same只有在strides=1时才是同大小
- out = tf.nn.conv2d(x,w,strides=1,padding='SAME')
- # %%
- x = tf.random.normal([2,5,5,3])
- w = tf.random.normal([3,3,3,4])
- # 高宽按3倍减少
- out = tf.nn.conv2d(x,w,strides=3,padding='SAME')
- print(out.shape)
- # %%
- # 根据[cout]格式创建偏置向量
- b = tf.zeros([4])
- # 在卷积输出上叠加偏置向量,它会自动broadcasting为[b,h',w',cout]
- out = out + b
- # %%
- # 创建卷积层类
- layer = layers.Conv2D(4,kernel_size=(3,4),strides=(2,1),padding='SAME')
- out = layer(x) # 前向计算
- out.shape
- # %%
- layer.kernel,layer.bias
- # 返回所有待优化张量列表
- layer.trainable_variables
- # %%
- from tensorflow.keras import Sequential
- network = Sequential([ # 网络容器
- layers.Conv2D(6,kernel_size=3,strides=1), # 第一个卷积层, 6个3x3卷积核
- layers.MaxPooling2D(pool_size=2,strides=2), # 高宽各减半的池化层
- layers.ReLU(), # 激活函数
- layers.Conv2D(16,kernel_size=3,strides=1), # 第二个卷积层, 16个3x3卷积核
- layers.MaxPooling2D(pool_size=2,strides=2), # 高宽各减半的池化层
- layers.ReLU(), # 激活函数
- layers.Flatten(), # 打平层,方便全连接层处理
- layers.Dense(120, activation='relu'), # 全连接层,120个节点
- layers.Dense(84, activation='relu'), # 全连接层,84节点
- layers.Dense(10) # 全连接层,10个节点
- ])
- # build一次网络模型,给输入X的形状,其中4为随意给的batchsz
- network.build(input_shape=(4, 28, 28, 1))
- # 统计网络信息
- network.summary()
- # %%
- # 导入误差计算,优化器模块
- from tensorflow.keras import losses, optimizers
- # 创建损失函数的类,在实际计算时直接调用类实例即可
- criteon = losses.CategoricalCrossentropy(from_logits=True)
- # %%
- # 构建梯度记录环境
- with tf.GradientTape() as tape:
- # 插入通道维度,=>[b,28,28,1]
- x = tf.expand_dims(x,axis=3)
- # 前向计算,获得10类别的预测分布,[b, 784] => [b, 10]
- out = network(x)
- # 真实标签one-hot编码,[b] => [b, 10]
- y_onehot = tf.one_hot(y, depth=10)
- # 计算交叉熵损失函数,标量
- loss = criteon(y_onehot, out)
- # 自动计算梯度
- grads = tape.gradient(loss, network.trainable_variables)
- # 自动更新参数
- optimizer.apply_gradients(zip(grads, network.trainable_variables))
- # %%
- # 记录预测正确的数量,总样本数量
- correct, total = 0,0
- for x,y in db_test: # 遍历所有训练集样本
- # 插入通道维度,=>[b,28,28,1]
- x = tf.expand_dims(x,axis=3)
- # 前向计算,获得10类别的预测分布,[b, 784] => [b, 10]
- out = network(x)
- # 真实的流程时先经过softmax,再argmax
- # 但是由于softmax不改变元素的大小相对关系,故省去
- pred = tf.argmax(out, axis=-1)
- y = tf.cast(y, tf.int64)
- # 统计预测正确数量
- correct += float(tf.reduce_sum(tf.cast(tf.equal(pred, y),tf.float32)))
- # 统计预测样本总数
- total += x.shape[0]
- # 计算准确率
- print('test acc:', correct/total)
- # %%
- # 构造输入
- x=tf.random.normal([100,32,32,3])
- # 将其他维度合并,仅保留通道维度
- x=tf.reshape(x,[-1,3])
- # 计算其他维度的均值
- ub=tf.reduce_mean(x,axis=0)
- ub
- # %%
- # 创建BN层
- layer=layers.BatchNormalization()
- # %%
- network = Sequential([ # 网络容器
- layers.Conv2D(6,kernel_size=3,strides=1),
- # 插入BN层
- layers.BatchNormalization(),
- layers.MaxPooling2D(pool_size=2,strides=2),
- layers.ReLU(),
- layers.Conv2D(16,kernel_size=3,strides=1),
- # 插入BN层
- layers.BatchNormalization(),
- layers.MaxPooling2D(pool_size=2,strides=2),
- layers.ReLU(),
- layers.Flatten(),
- layers.Dense(120, activation='relu'),
- # 此处也可以插入BN层
- layers.Dense(84, activation='relu'),
- # 此处也可以插入BN层
- layers.Dense(10)
- ])
- # %%
- with tf.GradientTape() as tape:
- # 插入通道维度
- x = tf.expand_dims(x,axis=3)
- # 前向计算,设置计算模式,[b, 784] => [b, 10]
- out = network(x, training=True)
- # %%
- for x,y in db_test: # 遍历测试集
- # 插入通道维度
- x = tf.expand_dims(x,axis=3)
- # 前向计算,测试模式
- out = network(x, training=False)
- # %%
- def preprocess(x, y):
- # [0~1]
- x = 2*tf.cast(x, dtype=tf.float32) / 255.-1
- y = tf.cast(y, dtype=tf.int32)
- return x,y
-
- # 在线下载,加载CIFAR10数据集
- (x,y), (x_test, y_test) = datasets.cifar100.load_data()
- # 删除y的一个维度,[b,1] => [b]
- y = tf.squeeze(y, axis=1)
- y_test = tf.squeeze(y_test, axis=1)
- # 打印训练集和测试集的形状
- print(x.shape, y.shape, x_test.shape, y_test.shape)
- # 构建训练集对象
- train_db = tf.data.Dataset.from_tensor_slices((x,y))
- train_db = train_db.shuffle(1000).map(preprocess).batch(128)
- # 构建测试集对象
- test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test))
- test_db = test_db.map(preprocess).batch(128)
- # 从训练集中采样一个Batch,观察
- sample = next(iter(train_db))
- print('sample:', sample[0].shape, sample[1].shape,
- tf.reduce_min(sample[0]), tf.reduce_max(sample[0]))
- # %%
- conv_layers = [ # 先创建包含多层的列表
- # Conv-Conv-Pooling单元1
- # 64个3x3卷积核, 输入输出同大小
- layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- # 高宽减半
- layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
- # Conv-Conv-Pooling单元2,输出通道提升至128,高宽大小减半
- layers.Conv2D(128, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.Conv2D(128, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
- # Conv-Conv-Pooling单元3,输出通道提升至256,高宽大小减半
- layers.Conv2D(256, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.Conv2D(256, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
- # Conv-Conv-Pooling单元4,输出通道提升至512,高宽大小减半
- layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
- # Conv-Conv-Pooling单元5,输出通道提升至512,高宽大小减半
- layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
- layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same')
- ]
- # 利用前面创建的层列表构建网络容器
- conv_net = Sequential(conv_layers)
- # %%
- # 创建3层全连接层子网络
- fc_net = Sequential([
- layers.Dense(256, activation=tf.nn.relu),
- layers.Dense(128, activation=tf.nn.relu),
- layers.Dense(100, activation=None),
- ])
- # %%
- # build2个子网络,并打印网络参数信息
- conv_net.build(input_shape=[4, 32, 32, 3])
- fc_net.build(input_shape=[4, 512])
- conv_net.summary()
- fc_net.summary()
- # %%
- # 列表合并,合并2个子网络的参数
- variables = conv_net.trainable_variables + fc_net.trainable_variables
- # 对所有参数求梯度
- grads = tape.gradient(loss, variables)
- # 自动更新
- optimizer.apply_gradients(zip(grads, variables))
- # %%
- x = tf.random.normal([1,7,7,1]) # 模拟输入
- # 空洞卷积,1个3x3的卷积核
- layer = layers.Conv2D(1,kernel_size=3,strides=1,dilation_rate=2)
- out = layer(x) # 前向计算
- out.shape
- # %%
- # 创建X矩阵
- x = tf.range(25)+1
- # Reshape为合法维度的张量
- x = tf.reshape(x,[1,5,5,1])
- x = tf.cast(x, tf.float32)
- # 创建固定内容的卷积核矩阵
- w = tf.constant([[-1,2,-3.],[4,-5,6],[-7,8,-9]])
- # 调整为合法维度的张量
- w = tf.expand_dims(w,axis=2)
- w = tf.expand_dims(w,axis=3)
- # 进行普通卷积运算
- out = tf.nn.conv2d(x,w,strides=2,padding='VALID')
- out
- #%%
- # 普通卷积的输出作为转置卷积的输入,进行转置卷积运算
- xx = tf.nn.conv2d_transpose(out, w, strides=2,
- padding='VALID',
- output_shape=[1,5,5,1])
- <tf.Tensor: id=117, shape=(5, 5), dtype=float32, numpy=
- array([[ 67., -134., 278., -154., 231.],
- [ -268., 335., -710., 385., -462.],
- [ 586., -770., 1620., -870., 1074.],
- [ -468., 585., -1210., 635., -762.],
- [ 819., -936., 1942., -1016., 1143.]], dtype=float32)>
- # %%
- x = tf.random.normal([1,6,6,1])
- # 6x6的输入经过普通卷积
- out = tf.nn.conv2d(x,w,strides=2,padding='VALID')
- out
- <tf.Tensor: id=21, shape=(1, 2, 2, 1), dtype=float32, numpy=
- array([[[[ 20.438847 ],
- [ 19.160788 ]],
- [[ 0.8098897],
- [-28.30303 ]]]], dtype=float32)>
- # %%
- # 恢复出6x6大小
- xx = tf.nn.conv2d_transpose(out, w, strides=2,
- padding='VALID',
- output_shape=[1,6,6,1])
- xx
- # %%
- # 创建转置卷积类
- layer = layers.Conv2DTranspose(1,kernel_size=3,strides=2,padding='VALID')
- xx2 = layer(out)
- xx2
- # %%
- class BasicBlock(layers.Layer):
- # 残差模块类
- def __init__(self, filter_num, stride=1):
- super(BasicBlock, self).__init__()
- # f(x)包含了2个普通卷积层,创建卷积层1
- self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=stride, padding='same')
- self.bn1 = layers.BatchNormalization()
- self.relu = layers.Activation('relu')
- # 创建卷积层2
- self.conv2 = layers.Conv2D(filter_num, (3, 3), strides=1, padding='same')
- self.bn2 = layers.BatchNormalization()
- if stride != 1: # 插入identity层
- self.downsample = Sequential()
- self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride))
- else: # 否则,直接连接
- self.downsample = lambda x:x
- def call(self, inputs, training=None):
- # 前向传播函数
- out = self.conv1(inputs) # 通过第一个卷积层
- out = self.bn1(out)
- out = self.relu(out)
- out = self.conv2(out) # 通过第二个卷积层
- out = self.bn2(out)
- # 输入通过identity()转换
- identity = self.downsample(inputs)
- # f(x)+x运算
- output = layers.add([out, identity])
- # 再通过激活函数并返回
- output = tf.nn.relu(output)
- return output
|