figure_draw_py

python绘图

环境配置与IDE配置

  • python环境安装可通过直接下载或者通过包管理器anaconda下载,这里更推荐使用anaconda可以搭建多个不同版本的python环境且彼此不受影响,两种方法下载后均会自动配置好环境变量,不需要下一步操作。后续介绍主要围绕anaconda+vscode的配置来介绍。
  • conda创建环境需要打开anaconda prompt来创建,常用的conda命令如下
    • conda env list 列出目前已有的环境
    • conda create -n env_name python=3.x.x 创建名称为env_name版本为3.x.x的python环境
    • conda activate env_name 进入env_name的环境 deactivate 退出环境
    • 在进入某一环境后可以通过conda install xxx来下载不同的包,原理与pip相同
  • IDE使用vscode,下载好后在任意一个目录下创建python文件后,在右下角选择已经创建好的conda环境即可

    常用包

  1. numpy 数据处理包,主要使用其中创建空数组或是对数组进行布尔逻辑索引等操作
  2. matplotlib python中绘图最主要的包
  3. xarray 可以读取.nc等带有表头格式的文件
  4. cartopy 主要进行地图绘制的包
  5. netCDF4 可以读取nc格式文件并将读取出来的文件转换为ndarray即numpy数组
  6. scipy 包含许多统计变量的计算函数
  7. h5py 读取HDF格式文件,读取后变为Dataset格式,通过提取变量变为ndarray
  8. shapefile 与shp文件相关的地图绘制

绘图基本流程与常用函数

以绘制GPM降水资料在甘肃的分布为例

  1. 读取数据
    import h5py
    file = 'path_to_GPM_file'
    Dataset = h5py.File(file)
    lon = Dataset['/Grid/lon'][:] #不同的二进制文件中变量关键词不同,变量组关系不同,可使用ncdump -h 先查看文件变量组在读取
    lat = Dataset['/Grid/lat'][:] #这里使用[:]是为了只将变量中的数据取出,而不包含变量的头信息等
    prec = Dataset['Grid/precipitationCal'][:]/2 #由于GPM是逐半小时的降雨率
    prec = prec[0,:,:].transpose() #由于GPM资料是time*lon*lat,需要转化为lat*lon再绘图
  2. 控制数据区域

由于GPM数据为全球范围,直接整体处理对内存与运行速度负载较大,可以通过python中numpy的特性MaskedArray与shp文件来限定其范围

import numpy as np
import shapefile as shp
mark = np.zeros((150,200),dtype=bool) #创建一个布尔形式与数据大小相符的数组
shp_file = shp.Reader('Gansu.shp')
rec = shp_file.shapeRecords()
for r in rec:
    poly = r.shape    #.shape属性一般需要通过迭代内部才能取到
for i in range(0,len(lon)):
    for j in range(0,len(lat)): #通过便利格点,来确定其是否在给定的shp文件范围内部
        if geometry.Point([lon[i],lat[j]]).within(geometry.shape(poly)):
            mark[j,i] = True # 根据shp文件结构不同,需要调整poly的结构,如这里使用的是甘肃省界shp文件,内部仅有一个多边形

到这里,mark数组就变为了在甘肃省内部为True,外部为False的一个标记数组,之后将经纬度网格点以及降水数据网格点取mark,即可绘制图像。这是python中较为常用的一个特性

  1. 绘图
  • 主要用到matplotlib下的pyplot包,需要在画图时将图中出现的字体改为新罗马

    from matplotlib import pyplot as plt #引入pyplot库
    import matplotlib as mpl
    mpl.rcParams['font.family'] = 'sans-serif'
    mpl.rcParams['font.sans-serif'] = ['Times New Roman']
    mpl.rcParams['axes.unicode_minus'] = False
    mpl.rcParams['mathtext.fontset'] = 'stix'
  • 主要的画图函数有

    plt.plot(x = ,y = ,color = ,linestyle = ) #折线图 可指定线条颜色,线条形式(实线 solid、虚线 dashed...)
    plt.contourf(x = , y = , z = , cmap = ) #填色图 z为每个格点上对应的值,注意,这里的xy必须为网格点数据,即不能是单一的xy一维序列,而是网格坐标,cmap可以修改填色所使用的colorbar
    import matplotlib.colors as mcolors
    clevs = [0,0.1,2,5,15,30,50]
    cdict = ['#00000000','#A9F090','#40B73F','#63B7FF','#0000FE','#FF00FC','#850042']
    prec_cmap = mcolors.ListedColormap(cdict)
    norm = mpl.colors.BoundaryNorm(clevs,prec_cmap.N) #可以使用这种方式自定义cmap,同样可以在scatter图中使用
    plt.scatter(x = , y = , c = , cmap = ,...) #注意,scatter中的xy与countourf不同,需为一维长度相等的数组,不可为二维网格
    plt.contour() #等值线图 同contourf
    plt.bar(x = , height = , width = ) #直方图 可以指定宽度或是高度随x而变化 
  • 绘图流程

可以通过plt直接调用几个函数直接进行绘制,或是通过

fig = plt.figure(figsize=(12,8))
ax = fig.addsubplot(111,projection = xxx) #这里的111是指一幅图,可以通过指定该参数来绘制组图如
ax1 = fig.addsubplot(2,2,1) # 此时添加了一副2*2的子图,并指定当前绘图区域在第一幅,即左上图,按照左到右,上到下排序
....
# 在绘图结束后,可以调用
plt.savefig(fanme = , dpi = , Orientation = , format = ) # 参数包括文件保存地址与文件名,文件清晰度(如保存位图可忽略),图片方向,图片格式
# 或是
plt.show() # 在弹出的对话框中选择保存的形式
  • 常用绘图工具函数
  1. 经纬度显示转化
from matplotlib.ticker import FuncFormatter
def to_latitude(temp, position):
    return '%1.0f'%(temp) + '°N'
def to_longtitude(temp,position): #通过占位符替换坐标轴显示形式,同理可以改写百分数
    return '%1.0f'%(temp)+ '°E'

需要在主函数,即绘图调用处加上,同理,改写百分制显示时只需要改写y轴显示形式即可

x_fomatter = FuncFormatter(to_longtitude)
y_fomatter = FuncFormatter(to_latitude)
plt.gca().yaxis.set_major_formatter(y_fomatter)
plt.gca().xaxis.set_major_formatter(x_fomatter)

py in met

写在前面

这篇博客主要记录在使用python进行气象数据处理/绘制中的一些问题
np.meshgrid(lon_grid,lat_grid) 可以将经纬度转换为对应的坐标点
xarray.Dataarray 具有标签属性(例如lat,lon)
numpy.array不具有标签属性

batch,epoch,iteration

  1. batch size:简单来说,batch size就是每次训练向模型中传入的数据量的多少,过小的话会导致梯度不明显,模型下降速度慢,过大的话尽管速度会更快但会导致需要更多的epoch来获得更好的结果,与batch本身的节约内存空间的目的相悖,因此需要选择一个合适的batch大小来进行训练。

  2. iteration:意思为迭代,一个iteration等于将batch size中的数据迭代一遍

  3. epoch:意思为周期,一个周期相当于将样本中的所有数据遍历一遍。例如样本数量为1000,batch size为10,那么就需要100此iteration来完成一个epoch。

MachineLearning

监督式学习

指目标给定标签来进行分类等例如聚类,分类问题等

无监督式学习

指目标没有指定的标签,通过已有样本的特点来进行预测拟合,如回归分析

构成

  1. 代价函数(一般为平方差等,表示拟合量与实际值的偏差)通过寻找代价函数与目标函数参数关系寻找到最小值来进行回归。

  2. 梯度下降法,通过遍历寻找在当前点梯度最大方向进行遍历,类似于贪心的思想,来降低代价函数值寻找局部最小值(注意,这里并不是全局的最小值),越大的学习系数每一步的步长越长,需要注意的是每次要做到每个系数的同步跟新。
    通过画出代价函数变化函数来确定是否收敛,一般在学习速率够小时,代价函数都会随着迭代而减小,但过小会导致收敛速度慢的问题

  3. 反向传播:包含两个阶段,激励传播与权重更新

    • 激励传播:包含两步,第一步前向传播阶段,将输入送入网络获得激励响应;第二部,求出代价函数,一般为方差获得输出层和隐藏层的误差
    • 权重更新:根据代价函数对于不同权重敏感程度以及各个激活值距离正确值的大小来决定,例如在有两个神经元一个激活值为0.2其期望值是1,一个是0.3其期望值是0,那么需要优先调整距离期望较大的神经元

TensorFlow2.x学习笔记

基础

创建变量等操作与1.x基本类似,但可以进行即时执行模式,与之前的图执行模式不同,语法更精炼简单。

num_epoch = 100000
optimizer = tf.keras.optimizers.SGD(learning_rate = 5e-4)
for e in range(num_epoch):
    with tf.GradientTape() as tape:
        y_pred = a * x + b
        loss = tf.reduce_sum(tf.square(y - y_pred))
    grads = tape.gradient(loss , variables)
    optimizer.apply_gradients(grads_and_vars = zip(grads , variables))

可以直接调用GradientTape来进行梯度以及导数计算。上面的代码中声明了一个梯度下降优化器,可以通过计算的求导结果更新模型参数,从而最小化某个特定的损失函数,通过apply_gradients()方法来进行调用,函数内的参数为需要更新的变量以及损失函数关于该变量的偏导数。需要传入一个列表,每个元素为(偏导,变量)。

模型与层

使用TF内置的库Keras来进行模型的构建。
Keras里有两个重要的概念:层(Layer)模型(model),层将各种计算流程和变量进行了封装(全连接层,卷积层,池化层等),模型则用于各种层进行连接。模型通过类的形式呈现,所以可以通过继承tf.keras.Model来定义自己的模型,需要重写init()和call(),继承模型后可以调用模型中的方法和属性。可以大量简化代码,下面是通过模型的方式编写的一个简单的线性回归,可以看出在进行计算处理部分与之前基本一致,但是关于模型变量的访问,以及模型初始化都方便了许多。通过在模型内部实例化了一个全连接层,并对其在call()中进行调用

X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
y = tf.constant([[10.0], [20.0]])

class Linear(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.dense = tf.keras.layers.Dense(
            units = 1,
            activation=None,
            kernel_initializer = tf.zeros_initializer(),
            bias_initializer = tf.zeros_initializer()
        )
    def call(self , input):
        output = self.dense(input)
        return output
model = Linear()
optimizer = tf.keras.optimizers.SGD(lr=0.01)
for i in range(100):
    with tf.GradientTape() as tape:
        y_pred = model(X)
        loss = tf.reduce_mean(tf.square(y_pred - y))
    grads = tape.gradient(loss , model.variables)
    optimizer.apply_gradients(grads_and_vars = zip(grads , model.variables))
print(model.variables)

全连接层(Dense) 是最常用的层之一,对输入矩阵进行f(AW+b)的线性变换+激活函数操作,激活函数一般是Relu等等.包含的参数如下

  • units 输出张量的维度
  • activation 激活函数 不指定时为f(x) = x
  • use_bias 添加偏置,默认为True
  • kernel_initializer,bias_initializer 权重,偏置的初始化器,默认为glort_uniform_initializer(很多层都默认使用),使用ezeros_initializer表示初始化为0

softmax函数为了使得模型的输出能始终满足这两个条件,我们使用 Softmax 函数 (归一化指数函数, tf.nn.softmax )对模型的原始输出进行归一化 。不仅如此,softmax 函数能够凸显原始向量中最大的值,并抑制远低于最大值的其他分量,这也是该函数被称作 softmax 函数的原因(即平滑化的 argmax 函数)。

模型的训练

tf.keras.losses 和 tf.keras.optimizer
需要定义一些模型超参数

  • num_epochs = 5

  • batch_size = 50

  • learning_rate = 0.001
    之后实例化模型和优化器,迭代进行数据的读取以及模型的训练步骤如下

  • 从 DataLoader 中随机取一批训练数据;

  • 将这批数据送入模型,计算出模型的预测值;

  • 将模型预测值与真实值进行比较,计算损失函数(loss)。这里使用 tf.keras.losses 中的交叉熵函数作为损失函数

  • 计算损失函数关于模型变量的导数;

  • 将求出的导数值传入优化器,使用优化器的 apply_gradients 方法更新模型参数以最小化损失函数

模型的评估

使用tf.keras.metrics中的SparseCategoricalAccuracy来评估模型在测试集上的性能,通过将预测结果与真是结果相比较,输出正确的占比。通过update_state()方法向评估器输入预测值和真实值。其内部有变量来保存当前评估的指标的数值,最终通过result()方法输出最终评估值。

代码汇总

import numpy as np
import tensorflow as tf
#CNN with keras
class CNN(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.conv1 = tf.keras.layers.Conv2D(
            filters=32,             # 卷积层神经元(卷积核)数目
            kernel_size=[5, 5],     # 感受野大小
            padding='same',         # padding策略(vaild 或 same)
            activation=tf.nn.relu   # 激活函数
        )
        self.pool1 = tf.keras.layers.MaxPool2D(pool_size=[2, 2], strides=2)
        self.conv2 = tf.keras.layers.Conv2D(
            filters=64,
            kernel_size=[5, 5],
            padding='same',
            activation=tf.nn.relu
        )
        self.pool2 = tf.keras.layers.MaxPool2D(pool_size=[2, 2], strides=2)
        self.flatten = tf.keras.layers.Reshape(target_shape=(7 * 7 * 64,))
        self.dense1 = tf.keras.layers.Dense(units=1024, activation=tf.nn.relu)
        self.dense2 = tf.keras.layers.Dense(units=10)

    def call(self, inputs):
        x = self.conv1(inputs)                  # [batch_size, 28, 28, 32]
        x = self.pool1(x)                       # [batch_size, 14, 14, 32]
        x = self.conv2(x)                       # [batch_size, 14, 14, 64]
        x = self.pool2(x)                       # [batch_size, 7, 7, 64]
        x = self.flatten(x)                     # [batch_size, 7 * 7 * 64]
        x = self.dense1(x)                      # [batch_size, 1024]
        x = self.dense2(x)                      # [batch_size, 10]
        output = tf.nn.softmax(x)
        return output
# MNISTLoader and MLP
class MNISTLoader():
    def __init__(self):
        mnist = tf.keras.datasets.mnist
        (self.train_data, self.train_label), (self.test_data, self.test_label) = mnist.load_data()
        # MNIST中的图像默认为uint8(0-255的数字)。以下代码将其归一化到0-1之间的浮点数,并在最后增加一维作为颜色通道
        self.train_data = np.expand_dims(self.train_data.astype(np.float32) / 255.0, axis=-1)      # [60000, 28, 28, 1]
        self.test_data = np.expand_dims(self.test_data.astype(np.float32) / 255.0, axis=-1)        # [10000, 28, 28, 1]
        self.train_label = self.train_label.astype(np.int32)    # [60000]
        self.test_label = self.test_label.astype(np.int32)      # [10000]
        self.num_train_data, self.num_test_data = self.train_data.shape[0], self.test_data.shape[0]

    def get_batch(self, batch_size):
        # 从数据集中随机取出batch_size个元素并返回
        index = np.random.randint(0, self.num_train_data, batch_size)
        return self.train_data[index, :], self.train_label[index]




# class MLP(tf.keras.Model):
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         self._flatten = tf.keras.layers.Flatten()
#         self.dense1 = tf.keras.layers.Dense(units=100 , activation = tf.nn.relu)
#         self.dense2 = tf.keras.layers.Dense(units=10)
#     def call(self, inputs):
#         x = self._flatten(inputs)
#         x = self.dense1(x)
#         x = self.dense2(x)
#         output = tf.nn.softmax(x)
#         return output
num_epochs = 5
batch_size = 50
learning_rate = 0.001
model = CNN()
data_loader = MNISTLoader()
optimizer = tf.keras.optimizers.Adam(learning_rate = learning_rate)
num_batches = int(data_loader.num_train_data // batch_size * num_epochs)
for batch_index in range(num_batches):
    X , y = data_loader.get_batch(batch_size)
    with tf.GradientTape() as tape:
        y_pred = model(X)
        loss = tf.keras.losses.sparse_categorical_crossentropy(y_true=y , y_pred=y_pred)
        loss = tf.reduce_mean(loss)
        print("batch %d: loss %f"%(batch_index , loss.numpy()))
    grads = tape.gradient(loss , model.variables)
    optimizer.apply_gradients(grads_and_vars = zip(grads , model.variables))
sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()
num_batches = int(data_loader.num_test_data // batch_size)
for batch_index in range(num_batches):
    start_index , end_index = batch_index * batch_size , (batch_index + 1) * batch_size
    y_pred = model.predict(data_loader.test_data[start_index : end_index])
    sparse_categorical_accuracy.update_state(y_true = data_loader.test_label[start_index : end_index] , y_pred= y_pred)
print("test accuracy : %f"%sparse_categorical_accuracy.result())
#using Model for linear regression
# X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
# y = tf.constant([[10.0], [20.0]])

# class Linear(tf.keras.Model):
#     def __init__(self):
#         super().__init__()
#         self.dense = tf.keras.layers.Dense(
#             units = 1,
#             activation=None,
#             kernel_initializer = tf.zeros_initializer(),
#             bias_initializer = tf.zeros_initializer()
#         )
#     def call(self , input):
#         output = self.dense(input)
#         return output
# model = Linear()
# optimizer = tf.keras.optimizers.SGD(lr=0.01)
# for i in range(100):
#     with tf.GradientTape() as tape:
#         y_pred = model(X)
#         print(model.variables)
#         loss = tf.reduce_mean(tf.square(y_pred - y))
#     grads = tape.gradient(loss , model.variables)
#     optimizer.apply_gradients(grads_and_vars = zip(grads , model.variables))
# print(model.variables)
# 1st the linear regression
# X_raw = np.array([2013, 2014, 2015, 2016, 2017], dtype=np.float32)
# y_raw = np.array([12000, 14000, 15000, 16500, 17500], dtype=np.float32)

# x = (X_raw - X_raw.min()) / (X_raw.max() - X_raw.min())
# y = (y_raw - y_raw.min()) / (y_raw.max() - y_raw.min())

# x = tf.constant(x)
# y = tf.constant(y)

# a = tf.Variable(initial_value = 0.)
# b = tf.Variable(initial_value = 0 , dtype = tf.float32)
# variables = [a,b]

# num_epoch = 100000
# optimizer = tf.keras.optimizers.SGD(learning_rate = 5e-4)
# for e in range(num_epoch):
#     with tf.GradientTape() as tape:
#         y_pred = a * x + b
#         loss = tf.reduce_sum(tf.square(y - y_pred))
#     grads = tape.gradient(loss , variables)
#     optimizer.apply_gradients(grads_and_vars = zip(grads , variables))
# print(a,b)

TensorFlow1.x学习笔记

debug

module ‘tensorflow’ has no attribute ‘Session’(已修复)

在新版本中Tensorflow 2.0版本中已经移除了Session这一模块,改换运行代码以及对于graph的语法皆以变化,语法近似pytorch

import tensorflow as tf
tf.compat.v1.disable_eager_execution()
config = tf.compat.v1.ConfigProto(allow_soft_placement=True)
sess= tf.compat.v1.Session(config=config)

建立一个简单的回归分析模型

  1. 首先使用placeholder为输入与输出创建占位符,同时为权重和截距创建合适的变量
x = tf.placeholder(tf.float32 , shape = [None ,3]) #输入
y_ture = tf.placeholder(tf.float32 , shape = None) #实际值
w = tf.Variable([[0,0,0]] , dtype = tf.float32 , 
name = 'weights')  #权重
b = tf.Variable(0 , dtype = tf.float32 , name = 'bias') #截距
y_pred = tf.matmul(w , tf.transpose(x)) + b #预测值
  1. 接下来,需要来评估模型的性能,为了刻画预测值与真实值的差异,需要订一个反应“距离”的度量,一般称为损失函数,通过寻找一组参数来最小化损失函数优化模型。一般使用MSE(均方差)和交叉熵
#-----------------MRE
loss = tf.reduce_mean(tf.square(y_ture - y_pred)) #MSE
#----------------交叉熵
loss = tf.nn.sigmod_cross_entropy_with_logits(lables = y_ture , logits = y_pred)
loss = tf.reduce_mean(loss)
  1. 接下来需要明白如何最小化损失函数,一般使用梯度下降法,尽管可能陷入局部最优,但是一般都是足够好的。

  2. 由于计算整个样本集合可能会很慢,所以需要使用一些采样方法,对样本的子集进行采样,一般规模在50~500个一次,过小的样本会使得硬件利用率降低,而且会使得目标函数产生较大的波动,但有时波动也是有益的,因为能够使得参数跳跃到新的局部最优值,TF中通过向图中添加新的操作然后使用自动差分来计算梯度,需要设置的就是学习率,来确定每次更新迭代的量,一般更倾向于设置较小的学习率,以防止跳过了局部最优解,但过低会导致损失函数减少的十分慢。

optimizer = tf.train.GradientDsecentOptimizer(learning_rate)
train = optimizer.minimize(loss)

线性回归

使用numpy生成数据,创建了三个特征向量样本,每个样本内积乘以一组权重加上偏差项再加上噪声得出结果,通过优化模型找到最佳参数


import numpy as np
import tensorflow.compat.v1 as tf
tf.disable_eager_execution()
x_data = np.random.randn(2000 , 3)
w_real = [0.3 , 0.5 , 0.1]
b_real = -0.2

noise = np.random.randn(1,2000)*0.1
y_data = np.matmul(w_real , x_data.T) + b_real + noise
#生成数据
NUM_STEPS = 11
wb = []
g = tf.Graph()
with g.as_default():
    x = tf.placeholder(tf.float32 , shape = [None , 3])
    y_true = tf.placeholder(tf.float32 ,shape=None)
    with tf.name_scope('inference') as scope:
        w = tf.Variable([[0,0,0]] , dtype = tf.float32 , name = 'weights')
        b = tf.Variable(0 , dtype = tf.float32 , name = 'bias')
        y_pred = tf.matmul(w , tf.transpose(x)) + b

    with tf.name_scope('loss') as scope:
        loss = tf.reduce_mean(tf.square(y_true - y_pred))

    with tf.name_scope('train') as scope:
        learning_rate = 0.5
        optimizer = tf.train.GradientDescentOptimizer(learning_rate)
        train = optimizer.minimize(loss)

    init = tf.global_variables_initializer()
    with tf.Session() as sess:
        sess.run(init)
        for step in range(NUM_STEPS):
            sess.run(train,feed_dict={x :x_data , y_true : y_data})
            if step % 5 == 0:
                print(step , sess.run([w,b]))
                wb.append(sess.run([w,b]))

逻辑回归

一般用来进行二分类任务,输出一个0-1之间地离散二值结果,通过sigmoid函数来将数值映射到0到1之间,之后通过阈值分类器来将0到1之间的值转换为0或1,与之前代码的唯一不同是损失函数部分

with tf.name_scope('loss') as scope:
loss = tf.nn.sigmoid_cross_entropy_with_logits(labels = y_true , logits = y_pred)
loss = tf.reduce_mean(loss)

参数

CNN

层中的神经元只与前一层中的一小块区域连接,而不是采取全连接方式,主要由输入层,卷积层,ReLU层,池化层和全连接层构成(一般将卷积层和ReLU层一起称为卷积层)具体说来,卷积层和全连接层(CONV/FC)对输入执行变换操作的时候,不仅会用到激活函数,还会用到很多参数,即神经元的权值w和偏差b;而ReLU层和池化层则是进行一个固定不变的函数操作。卷积层和全连接层中的参数会随着梯度下降被训练,这样卷积神经网络计算出的分类评分就能和训练集中的每个图像的标签吻合了。

卷积层

参数由一些可学习的滤波器集合构成,只观察输入数据中的一小部分,由于卷积有“权值共享”这样的特性,可以降低参数的数量,防止参数过多造成过拟合。每个神经元连接的空间大小叫做神经元的感受野,深度与输入相同,但宽高是局部的。输出数据体和使用的滤波器的数量一致,将沿着深度方向排列。有时候在输入数据体的边缘使用0进行填充,使得滤波器可以平滑地在数据上滑动,一般是用来保持数据体的空间尺寸使输入输出宽高相同。如果在一个深度切片中的所有权重都使用同一个权重向量,那么卷积层的前向传播在每个深度切片中可以看做是在计算神经元权重和输入数据体的卷积(这就是“卷积层”名字由来)。这也是为什么总是将这些权重集合称为滤波器(filter)(或卷积核(kernel)),因为它们和输入进行了卷积。简单来说,就是一个在原始数据上以步长为长度不断移动的一个矩阵,层数与原始数据一致,将每个对应位置的数据进行乘积并且将每层的值相加,即为输出的一个数据,例如RGB通道下就是三层滤波器所得相加填入新的矩阵当中。

池化层

用来简化数据,减少计算量,有最大池化和平均池化,使用较多的是最大池化,通过找出某一个区域内的最大值/平均值来缩小数据,一般使用的参数是f=2,p=2 恰好为缩小数据为原来的一半,并且反向传播没有参数适用于池化,这一过程是静态过程,参数都是手动设定,或是交叉验证得到的,只用于简化数据,提取特征。

全连接层

将池化层简化后的数据进行提取,将特征整合到一起,输出为一个值,主要作用是减小特征位置对于结果产生的影响,将结果进行分类,一般不止一层,需要将提取出来的特征神经元激活后,通过不同神经元激活的组合,再提取出结果

简单的卷积实现mnist识别,将测试过程分为每个大小为1000幅图的十块

import tensorflow.compat.v1 as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
DATA_DIR = '/tmp/data'
NUM_STEPS = 10000
MINIBATCH_SIZE = 100
tf.disable_eager_execution()
def weight_variable(shape):
    initial = tf.truncated_normal(shape , stddev=0.1)
    return tf.Variable(initial)
def bias_variable(shape):
    initial = tf.constant(0.1 , shape = shape)
    return tf.Variable(initial)
def conv2d (x , w):
    return tf.nn.conv2d(x , w , strides = [1, 1 ,1 ,1] , padding = 'SAME')
def max_pool_2x2(x):
    return tf.nn.max_pool(x , ksize = [1 , 2, 2,1], strides = [1,2,2,1] , padding='SAME')
def conv_layer(input , shape):
    w = weight_variable(shape)
    b = bias_variable([shape[3]])
    return tf.nn.relu(conv2d(input,w)+b)
def full_layer(input , size):
    in_size = int(input.get_shape()[1])
    w = weight_variable([in_size,size])
    b = bias_variable([size])
    return tf.matmul(input,w) + b
x = tf.placeholder(tf.float32 , shape=[None , 784])
y_ = tf.placeholder(tf.float32 , shape=[None , 10])
x_image = tf.reshape(x,[-1,28,28,1])
conv1 = conv_layer(x_image , shape = [5,5,1,32])
conv1_pool = max_pool_2x2(conv1)

conv2 = conv_layer(conv1_pool , shape = [5,5,32,64])
conv2_pool = max_pool_2x2(conv2)

conv2_flat = tf.reshape(conv2_pool, [-1,7*7*64])
full_1 = tf.nn.relu(full_layer(conv2_flat , 1024))

keep_prob = tf.placeholder(tf.float32)
full1_drop = tf.nn.dropout(full_1 , keep_prob = keep_prob)

y_conv = full_layer(full1_drop , 10)

minst = input_data.read_data_sets(DATA_DIR , one_hot=True)
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = y_conv , labels = y_) )
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv , 1) , tf.arg_max(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction , tf.float32))
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    for i in range(NUM_STEPS):
        batch = minst.train.next_batch(50)

        if i % 100 == 0:
            train_accuracy = sess.run(accuracy , feed_dict={x:batch[0] , y_:batch[1] , keep_prob : 1.0})
            print("step {} , train accuracy {}".format(i,train_accuracy))
        sess.run(train_step , feed_dict={x:batch[0] , y_:batch[1] , keep_prob : 0.5})
        X = minst.test.images.reshape(10,1000,784)
        Y = minst.test.labels.reshape(10,1000,10)
        test_accuracy = np.mean([sess.run(accuracy , feed_dict={x:X[i] , y_:Y[i] , keep_prob : 1.0}) for i in range(10)])
    print("test accuracy: {}".format(test_accuracy))

ResNet

深度残差网络(Deep residual network, ResNet)的提出是CNN图像史上的一件里程碑事件.通过引入残差学习的方法来解决网络在随着深度增加时出现的准确度饱和下降,梯度爆炸以及消失的难以训练的问题。对于一个堆积结构,在输入x时,学到的特征为H(x),现在希望其学到残差即 H(x)-x,这样即使残差为0时,也仅是在层之间做了恒等映射,至少不会性能下降,残差使得其会在本有的特征上学习到新的特征,有点类似电路中的短路`

练手题目

写在开头

写这篇博客旨在把平日练手的一些有趣的小题分享,很多的题应该都不会涉及复杂的算法只是一些想法比较有意思的小题

1.一串数字,长度未知,求其中只出现一次的数。要求时间复杂度O(1),空间复杂度O(0)

一开始看到这道题想了用标记数组来做,后在看视频的时候无意间看到了”^”这个符号想起来了用位运算来处理每一个数,如果两个数相同异或就会变成0,任何数和0异或又是
其本身,代码如下**

#include<stdio.h>
int main()
{
    int n,ans=0;
    while(EOF!=scanf("%d",&n))
    {
        ans^=n;
    }
    printf("%d\n",ans);
    return 0;
}

2.给出三个队列 s1,s2,s3 ,判断 s3 是否是由 s1 和 s2 交叉得来。 如:s1 为 aabcc , s2 为 dbbca。 当 s3 aadbbcbcac 时,返回 true(即将 s1 拆成三部分: aa,bc,c 分别插入 s2 对应位置) 否则返回 false。**

一开始想到就是字符串匹配问题,代码如下,但没有考虑,前一串以最优情况匹配,后一串在匹配时会因前一串把优先把靠前位置都占据,而出现匹配不
上的情况,因此加以优化,将第一串从后往前匹配,将更优的位置给下一串留下,中途甚至想过用每个字符的ASCII码建一个数组,然后只用匹配数量即可,但后来发现这样匹配没有考虑到字符串本来的顺序问题,放弃。

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
char a[100];
char b[100];
char c[200];
int ans[200];
char d[400];
int main()
{
    memset(ans,0,sizeof(ans));
    int i = 0 ,flag = 1 ,mark = 1 , m = 0 , n = 0 ,pos;
   scanf("%s",d);
   getchar();
   while(d[i]!=',')
   {
       a[i] = d[i];
       i++;
   }
   a[i] = '\0';
   i++;
   while(d[i]!=',')
   {
       b[m++] = d[i++];
   }
   b[m] = '\0';
   i++;
   int len = strlen(d);
   while(i<len)
   {
       c[n++] = d[i++];
   }
   c[n] = '\0';
   int lena = strlen(a);
   pos = lena-1;
   for(int j = n-1 ; j >=0 ; j--)
   {
       if(c[j]==a[pos])
       {
           ans[j] = 1;
           pos--;
       }
   }
   if(pos!=-1)
   {
       flag = 0;
   }
   pos = 0;
   for(int j = 0 ; j < n ; j++)
   {
       if(c[j]==b[pos]&&ans[j]!=1)
       {
           pos++;
       }
   }

   if(pos != m)
   {
       mark = 0;
   }
   if(mark&&flag)
   {
       printf("true\n");
   }
   else{
    printf("false\n");
   }
   return 0;
}