运行demo之后续补充
运行CPU版,看各部分工作原理
致命错误:
为了使占用内存不至于过高,使用了batch_size的融合方法
这是与源代码不同的地方
但是,这也造成了生成图像成了小方块
即:test的输入只能是完整图像
这个理解错了,具体原因在XMind中分析
在运行…
merge函数中
img[120X5,120X7]是总画布
img[0:120,0:120,:] 放image[120,120,1]
配置更改:
epochs=2
train_ir等的图片设置为1张
训练结束,想要生成图像,要更改main函数中:is_train->False,操作如下
srcnn.train(FLAGS)
FLAGS.is_train = False
srcnn.train(FLAGS)
代码更改:
util.py第203行添加
padding_h,padding_w = int(padding_h),int(padding_w)
test部分,代码更改如下:
print("Testing...")
if not self.load(config.checkpoint_dir): return
result = np.zeros(shape=train_label_ir.shape) #用于存储生成的图像的白板
len_data = len(train_data_ir)
batch_idxs = len_data // config.batch_size
for idx in range(0, batch_idxs):
idx_start = idx * config.batch_size
idx_end = min( (idx + 1) * config.batch_size, len_data )
batch_images_ir = train_data_ir[idx_start:idx_end]
batch_labels_ir = train_label_ir[idx_start:idx_end]
batch_images_vi = train_data_vi[idx_start:idx_end]
batch_labels_vi = train_label_vi[idx_start:idx_end]
temp_result = self.fusion_image.eval(
feed_dict={self.images_ir: batch_images_ir, self.labels_ir: batch_labels_ir,
self.images_vi: batch_images_vi,self.labels_vi: batch_labels_vi})
result[idx_start:idx_end,:,:,:] = temp_result
# result = result * 127.5 + 127.5
result = merge(result, [nx_ir, ny_ir])
原来的代码是:
result = self.fusion_image.eval(
feed_dict={self.images_ir: train_data_ir, self.labels_ir: train_label_ir,
self.images_vi: train_data_vi, self.labels_vi: train_label_vi})
load函数
了解:取得path最后的文件名os.path.basename()方法
加载模型使用了方法一
try:
self.sess.run(tf.global_variables_initializer())
self.saver.restore(self.sess, os.sep.join([checkpoint_dir,'other_model','CGAN.model-3']))
print("Model restored !")
return True
except Exception as e:
print("Restore Error :",e)
return False
解释:
- 1张输入的裁剪:
batchsize=32
1张输入图片:768X576
每个ep: 1472个图像需要训练,总批数:batch_idxs=46
总批数计算方法:
total_num =int((576-132)/14+1)*int((768-132)/14+1)
batch_idxs = total_num // batchsize
- 生成图片的保存位置:
sample/test_image.png
- 生成模型的保存(每个ep保存一次)
checkpoint/CGAN_120/...
步骤:
input_setup存为h5数据,再由train函数读出
取得每个批的图片(详细见下面)
训练2次d,1次g
每10批输出一次训练情况
Training...
Epoch: [ 1], step: [10], time: [2607.9601], loss_d: [0.92256868],loss_g:[15.22666168]
####解除bug的封印
-
缺乏tf.python.core
pip install tensorflow-estimator==1.15.0
-
内存不足问题Windows MemoryError: Unable to allocate 6.38 GiB for an array with shape (3
-pychar跑程序未使用cuda -
Backend Qt5Agg is interactive backend. Turning interactive mode on.
总览概括
捋一捋思路
main函数中,先预定义了超参数,然后建立了模型CGAN(),紧接着进行了训练train()
build_model分为好几步:对Ir,Vi设立占位符,各包含image(原料)和label(对抗)
输入之前,对Ir和Vi各自的image进行了拼接,这样channel变成2
然后生成融合图像fusion
d_loss部分,根据label得到pos损失,根据fusion得到neg损失
g_loss部分,分别得到g_loss_1欺骗损失和g_loss_2辐射相似性及梯度相似性train部分需要一些处理:
- 准备数据:
- 训练模式下:
将dataset(Train_ir,Train_vi)文件夹下的bmp,tif找出并排序
读入照片,将图像平移为0中心,并归一化
按14步长取输入图像patch,image:3333,label:2121
将它们作为准备好的输入数据
使用np.asarray() 转换成 dtype 数据
制作h5数据,放在./checkpoint_20/Train_ir/train.h5- 测试模式下:
将dataset(Test_ir,Test_vi)文件夹下的bmp,tif找出并排序
填充测试图像t1: 左padding,右填充直到33; 下padding,上填充直到33
将填充后的图像划分为若干个33*33的输入图像i2,并以1个图像作为一个输入单元shape=(33,33,1)
制作h5数据(./checkpoint_20/Test_ir/test.h5)
返回测试图像t1的真实尺寸,后面会以此为根据复原融合的图
读出h5数据,作为输入
找出变量组,包括Generator和discriminator的(由前面由
with tf.variable_scope('discriminator'): ...
语句生成)设置优化操作,
tf.train.AdamOptimizer().minimize(self.g_loss_total,var_list=self.g_vars)
.降低损失是将要训练的目的开始训练:
- 共 batch_idxs 个批次,每个批次batch_size个输入,每个输入有images_ir,labels_ir,images_vi,labels_vi
- 判别器2次:每次一个batch_size喂入,计算[discriminator变量组,d_loss]
生成器1次:每次一个batch_size喂入,计算[Generator变量组,g_loss_total,]- 每10次输出一次比较
- 保存模型:
训练时在路径’CGAN_21/CGAN.model’,保存检查点文件
- 开始测试
result = self.fusion_image.eval(feed_dict={})
- 将结果拼接起来
保存图片
对代码的改动和注解
廓清概念
image_size: Iir 和 Ivi 的尺寸
label_size: generator生成的尺寸
label图像,有vi的和ir的作用之一:在判别器中与If对抗
作用之二:计算g_loss中Lcontent一项,它包括If和Iir的相似性,以及If梯度和Ivi梯度的相似性
utils中代码的注解:
- scope.reuse_variables()
验证集通常是取训练集中的一小部分数据来检测训练的准确率,要共享之前训练好的相关参数的值。这种共享方式,需要在 变量命名空间 tf.variable_scope 下进行,而且采用 tf.get_variable 的共享变量定义方式,在共享之前,必须声明以下的参数是需要共享的,否则会重新定义。
with tf.variable_scope("inference") as scope:
train_y_conv =7 inference(train_images) #先训练
scope.reuse_variables() #说明以下验证集的参数是要共享训练集训练好的参数,而不是重新定义
test_y_conv = inference(test_images) #计算验证集的值
reuse的另一种用法:直接放入scope参数
with tf.variable_scope('discriminator', reuse=reuse):
- 对W进行的操作
指定维度求和
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGrB1hXK-1628232138446)(file:///D:\PythonFiles\paper02_FusionGAN_master\Others\tf_reduce_sum.jpg)]
L2范数:平方-求和-取开方
参考博文
范数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zk9b3ozk-1628232138448)(file:///D:\PythonFiles\paper02_FusionGAN_master\Others\l.jpg)].
L2范数: 可以用作正则项(防止过拟合)或损失函数(最小二乘误差least squares error, LSE)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e7JfeFo7-1628232138449)(file:///D:\PythonFiles\paper02_FusionGAN_master\Others\l2.jpg)]
本文代码待解读:像是缩放(看论文)
add_to_collection(名称,值)
将值存储在具有给定名称的集合中。
请注意,集合不是set(去重),因此可以多次向集合添加值。
参数:
名称:集合的键。 GraphKeys 类包含许多集合的标准名称。
value:要添加到集合中的值。
为Graph的一个方法,可以简单地认为Graph下维护了一个字典,key为name,value为list,而add_to_collection就是把变量添加到对应key下的list中
操作的含义待解
3. lrelu:把x<0的区域抬起来一些 比如0.2时,抬起80%
tensorflow中:
tf.nn.relu(features, name=None)
tf.nn.leaky_relu(features, alpha=0.2, name=None)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SSi9yrOI-1628232138450)(file:///D:\PythonFiles\paper02_FusionGAN_master\Others\relu.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3vDBbOg7-1628232138451)(file:///D:\PythonFiles\paper02_FusionGAN_master\Others\lrelu.png)]
- input_setup: np.lib.pad
arr = np.array([[4, 3],[4, 2]])
arr_pad = np.lib.pad(arr,((3,2),(2,1)),'constant',constant_values=0)
表示:在上面填充了3行,下面填充了2行,前面填充了2列,后面填充了1列。填充的都是 常量,0
若是图片填充,参考
model模块中的注解:
- tf.Variable()和tf.get_variable()的区别:
Variable是定义变量,而get_variable是获取变量(只不过如果获取不到就重新定义一个变量),如果按照这种逻辑,已经基本上可以理解两者的差异了
tf.Variable 每次都会创建新的变量,在变量名重复的时候不会报错,而是会自动创建新的变量,只是后缀加上 _1, _2 类似的用以区别。通常 lr 或 global step 之类的辅助变量会使用它来创建。tf.get_variable() 则主要用于网络的权值设置,它可以实现权值共享,在多 GPU 的并行计算的时候使用较多,其实通过 get 的前缀就可以很好看出它们的区别,它一定是和tf.variable_scope()共同使用的,不然二者就没有太大的区别了。
def get_variable(name,shape=None,dtype=None,initializer=None,...):
常见的initializer有:常量初始化器tf.constant_initializer、正太分布初始化器tf.random_normal_initializer、截断正态分布初始化器tf.truncated_normal_initializer、均匀分布初始化器tf.random_uniform_initializer。
-
tf.name_scope(‘IR_input’) 给变量"包"一层名字,方便变量管理
代码运行见test.py -
tf.concat与tf.stack都是在某个维度上对矩阵(向量)进行拼接,不同点在于前者拼接后的矩阵维度不变,后者则会增加一个维度。
拼接:第二个接在第一个后面
a = tf.constant([[1,2,3],[4,5,6]])
b = tf.constant([[7,8,9],[10,11,12]])
ab1 = tf.concat([a,b],axis=0)
print(sess.run(ab1).shape) # (4, 3)
ab2 = tf.stack([a,b], axis=0)
print(sess.run(ab2).shape) # (2, 2, 3)
4.Batch Normalization :为了加速训练对数据进行预处理,最常用的是零均值和PCA(白化).随着网络层次加深,导致每层间以及不同迭代的相同层的输入分布发生改变,导致网络需要重新适应新的分布,迫使我们降低学习率降低影响。有些人首先提出在每层增加PCA(先对数据进行去相关然后再进行归一化),但计算量大.BN是近似白化预处理。
概括:减均值->除标准差->加个性化参数γ、β
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HwLP9ydl-1628232138452)(file:///D:\PythonFiles\paper02_FusionGAN_master\Others\BN.jpg)]
还可参考
两个参数γ、β。这样在训练过程中就可以学习这两个参数,采用适合自己网络的BN公式
tf.contrib.layers.batch_norm(
inputs,decay=0.999,center=True,scale=False,epsilon=0.001,activation_fn=None,updates_collections=tf.GraphKeys.UPDATE_OPS,...
)
inputs: 输入
decay :衰减系数。合适的衰减系数值接近1.0,特别是含多个9的值:0.999,0.99,0.9。如果训练集表现很好而验证/测试集表现得不好,选择小的系数(推荐使用0.9)。如果想要提高稳定性,zero_debias_moving_mean设为True
center:是否有偏移?如果为True,有b偏移量;如果为False,无b偏移量
scale:input是否有缩放?如果为True,则乘以gamma。如果为False,gamma则不使用。当下一层是线性的时(例如nn.relu),由于缩放可以由下一层完成,所以可以禁用该层。
epsilon:避免被零除
activation_fn:用于激活,默认为线性激活函数
updates_collections :Collections来收集计算的更新操作。updates_ops需要使用train_op来执行。如果为None,则会添加控件依赖项以确保更新已计算到位。
tf.GraphKeys 包含所有graph collection中的标准集合名
graph collection 即tf.Graph,包含两类相关信息:
图结构 ,图集合(tf.add_to_collection函数允许您将对象列表与一个键相关联(其中tf.GraphKeys定义了部分标准键),tf.get_collection则允许您查询与键关联的所有对象。
TensorFlow库的许多组成部分会使用它:例如,当您创建tf.Variable时,系统会默认将其添加到表示“全局变量(tf.global_variables)”和“可训练变量tf.trainable_variables)”的集合中。当您后续创建tf.train.Saver或tf.train.Optimizer时,这些集合中的变量将用作默认参数。
这部分collection的名字被称为tf.GraphKeys,可以用来获取不同类型的op
常见的GraphKeys
- tf.nn.conv2d
tf.nn.conv2d (input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
input : 输入的要做卷积的图片,要求为一个张量,shape为 [ batch, in_height, in_width, in_channel ],其中batch为图片的数量,in_height 为图片高度,in_width 为图片宽度,in_channel 为图片的通道数,灰度图该值为1,彩色图为3。(也可以用其它值,但是具体含义不是很理解)
filter: 卷积核,要求也是一个张量,shape为 [ filter_height, filter_width, in_channel, out_channels ],其中 filter_height 为卷积核高度,filter_width 为卷积核宽度,in_channel 是图像通道数 ,和 input 的 in_channel 要保持一致,out_channel 是卷积核数量。
strides: 卷积时在图像每一维的步长,这是一个一维的向量,[ 1, strides, strides, 1],第一位和最后一位固定必须是1
padding: string类型,值为“SAME” 和 “VALID”,表示的是卷积的形式,是否考虑边界。"SAME"是考虑边界,不足的时候用0去填充周围,"VALID"则不考虑
use_cudnn_on_gpu: bool类型,是否使用cudnn加速,默认为true
博文链接
- 卷积输出大小计算计算公式
out_size = (w - k + 2p)/s + 1
w:Width ,
k: Kernel_size,
p:Padding_len , 因为一般两边都填充,所以2*p
s: stride_size
- d_loss = pos_loss + neg_loss
Ld = 1/N (生成样本bs个:'每个样本If预测值' - '软_标签值0~0.3' 的平方求和) + 1/N (可见光样本bs个:'每个样本Ivi预测值' - '软_标签值0.7~1.2' 的平方求和)
pos_loss = tf.reduce_mean(
tf.square(pos - tf.random_uniform(shape=[self.batch_size, 1], minval=0.7, maxval=1.2)))
neg_loss = tf.reduce_mean(
tf.square(neg - tf.random_uniform(shape=[self.batch_size, 1], minval=0, maxval=0.3, dtype=tf.float32)))
tf.reducemean()
tf.reduce_mean(input_tensor, axis=None, keep_dims=False, name=None, reduction_indices=None)
根据给出的axis在input_tensor上求平均值。除非keep_dims为真,axis中的每个的张量秩会减少1。如果keep_dims为真,求平均值的维度的长度都会保持为1.如果不设置axis,所有维度上的元素都会被求平均值,并且只会返回一个只有一个元素的张量。
见test.py代码 函数名:reduce_mean
对axis的理解: 2维中,有行和列,先行后列,行为第1维,因为从axis从0起编号,因此也最小(axis=0).列为第二维,可以理解为次要维.当收缩时,axis=1即收缩次要维.
当很多维的时候,也可以看数组,从最外层开始,往深层次走,理解为重要性依次减弱.那么,如果axis=-1(最后一个),那么便是收缩最次要维,也就是最里面一个.
tf.summary.scalar()的用法:
这个方法是添加变量到直方图中,但是不配合其他的方法,根本就显示不出来它的意义!
基本使用步骤:
定义变量;
让变量添加到summary.scalar;
将summary保存到磁盘以便tensorboard显示;
把步骤都记录下;
cmd命令,切换到相应的文件夹下,启动tensorborder;
然后再页面上输入localhost:6006
使用样例
summary_writer = tf.summary.FileWriter('E://result/', flush_secs=60)
summary_writer.add_graph(sess.graph)#添加graph图
tf.summary.scalar('loss', loss)
tf.summary.scalar('accuracy', accuracy) ##生成准确率标量图
sum_ops = tf.summary.merge_all() #自动管理
metall = sess.run(sum_ops, feed_dict={X: batch_x_test, Y: batch_y_test, keep_prob: 1.})
summary_writer.add_summary(metall, global_step=step) # 写入文件
- g_loss = g_loss_1 + g_loss_2
g_loss_1 = 1/N (生成样本bs个:'每个样本If预测值' - '软_标签值0.7~1.2' 的平方求和)
代表预测值和Generator想要Discriminator认为的值之间的差距.
即:融合图像和真实图像间的差距
g_loss_2 = (融合图像If-红外图像Ir)的元素的平方的和的平均 + 5 * (If梯度-Ivi梯度)的元素的平方的和的平均
tf.placeholder构筑静态graph -> 启动一个session ->运行模型时feed_dict={…}喂数据
这样做的好处在于:tensorflow帮你优化整个系统的代码
tf.placeholder(
dtype,
shape=None,
name=None
)
shape:数据形状。默认是None,就是一维值,也可以是多维(比如[2,3], [None, 3]表示列是3,行不定)
示例
import tensorflow as tf
import numpy as np
x = tf.placeholder(tf.float32, shape=(1024, 1024))
y = tf.matmul(x, x)
with tf.Session() as sess:
#print(sess.run(y)) # ERROR:此处x还没有赋值
rand_array = np.random.rand(1024, 1024)
print(sess.run(y, feed_dict={x: rand_array}))
Frobenius范数: 矩阵元素的平方和再开方
f范数实际上就是衡量这个矩阵和对应的零矩阵的距离
-文中用于比较真实矩阵和估计矩阵之间的相似性
max_to_keep 参数表示要保留的最近检查点文件的最大数量
参考这里
保存:
tf.train.Saver(max_to_keep=50)
saver.save(sess, '路径 + 模型文件名')
载入:
saver = tf.train.Saver()
ckpt = tf.train.get_checkpoint_state(model_save_path)
# 载入模型,不需要提供模型的名字,会通过 checkpoint 文件定位到最新保存的模型
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
- train 函数
glob模块用来查找文件目录和文件,返回列表,如下是用来查找e盘中所有的exe文件
glob.glob(r'e:\*.exe')
os.sep: 根据所处的平台,自动采用相应的分隔符号
data_dir = os.sep.join(['hello', 'world'])
得到linux: 'hello/world'或者win: 'hello\world'
图像插值函数原型
sub_input = cv2.resize(sub_input, (config.image_size / 4, config.image_size / 4),interpolation=cv2.INTER_CUBIC)
cv2.resize(InputArray src, OutputArray dst, Size, fx, fy, interpolation)
INTER_CUBIC: 4x4像素邻域的双三次插值
with h5py.File(savepath, 'w') as hf:
hf.create_dataset(name , value)
with h5py.File(savepath, 'r') as hf:
hf.get(name)
#示例
with h5py.File(savepath, 'w') as hf:
hf.create_dataset('data', data=data)
hf.create_dataset('label', data=label)
with h5py.File(path, 'r') as hf:
data = np.array(hf.get('data'))
label = np.array(hf.get('label'))
return data, label
tf.train.AdamOptimizer
self.train_fusion_op = tf.train.AdamOptimizer(config.learning_rate).minimize(self.g_loss_total,var_list=self.g_vars)
函数原型为,参考
__init__(
learning_rate=0.001,...
)
可调用的方法为:
.minimize(
loss,
global_step=None,
var_list=None, #会通过更新var_list添加操作以最大限度地最小化 loss
gate_gradients=GATE_OP,
aggregation_method=None,
colocate_gradients_with_ops=False,
name=None,
grad_loss=None
)
main中的注解:
1. 在tensorflow中,tf.app.flags.FLAGS已经转移到tf.flags.FLAGS路径下
FLAGS = tf.app.flags.FLAGS
FLAGS = tf.flags.FLAGS
2. tf.app.flags.FLAGS: 在用命令行执行程序时,需要传些参数
参数为key、默认值、和参数描述
# tf.app.flags.DEFINE_string("param_name", "default_val", "description")
tf.flags.DEFINE_string("key1","hello","这是一个字符串")
tf.flags.DEFINE_xxx()就是添加命令行的optional argument(可选参数),而tf.flags.FLAGS可以从对应的命令行参数取出参数。
3. 更改缩进:停留光标在~~~上,会有选项
额外补充
Tensorflow的运行机制包括: 定义计算图和运行计算图两个部分,计算图即模型的构建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ndd861Fc-1628232138453)(file:///D:/PythonFiles/paper02_FusionGAN_master/Others/tf_def.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L58o78gk-1628232138453)(file:///D:/PythonFiles/paper02_FusionGAN_master/Others/tf_process.jpg)]
先定义placeholder、Variable和OP等构成一张完成计算图Graph;
接下来通过新建Session实例启动模型运行,Session实例会分布式执行Graph;
输入数据,根据优化算法更新Variable,然后返回执行结果即Tensor实例。\
一个使用样例:
import tensorflow as tf # 引入tensorflow相关包
constant_a = tf.constant('Hello World!') # 定义常量
with tf.Session() as session:
print(session.run(constant_a)) # 运行图,并获取constant_a的执行结果
注入机制: 用户通过placeholder定义占位符并构建完整Graph后,利用Session实例.run将训练/测试数据注入到图中,驱动任务的实际运行
import tensorflow as tf # 引入tensorflow相关包
placeholder_a = tf.placeholder(tf.float32) # 定义placeholder实例
placeholder_b = tf.placeholder(tf.float32)
add_result = tf.add(placeholder_a, placeholder_b) # OP使两值相加
multiply_result = tf.multiply(placeholder_a, placeholder_b) # OP使两值相加
with tf.Session() as session:
# 运行图,获取执行结果
print(session.run(add_result, feed_dict = {placeholder_a: 1.0, placeholder_b: 2.0})) # 获取单个值
print(session.run([add_result, multiply_result], feed_dict = {placeholder_a: 3.0, placeholder_b: 4.0})) # 获取多个值
session的参数有:target(指定硬件设备),graph(适用于多计算图的场景,指定运行的计算图),config(定义session运行配置,用tf.ComfigProto配置)
sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))
###首先,填的一些小坑:导入包和版本代码修改
pip3 install tensorflow-gpu==1.15
pip install opencv-python==4.1.1.26
pip install scipy==1.2.1
修改了:xrange 为 range
FusionGAN
FusionGAN 的代码,一种用于红外和可见光图像融合的 GAN 模型
请参阅我们的以下论文以了解算法详细信息:
gpu1.15
pip install opencv-python4.1.1.26
pip install scipy==1.2.1
修改了:xrange 为 range
参考论文,以了解算法详细信息:
马嘉仪、魏宇、彭伟梁、常丽和江俊军,《“FusionGAN:用于红外和可见光图像融合的生成对抗网络”》,信息融合,48,第 11-26 页,2019 年 8 月。