当前位置:   article > 正文

Keras下的多GPU训练和测试——以U-net为例_u_net.fit默认使用gpu训练吗

u_net.fit默认使用gpu训练吗

先上主函数代码:

  1. # -*- coding: utf-8 -*-
  2. from model import *
  3. from data import *#导入这两个文件中的所有函数
  4. from keras.utils import multi_gpu_model
  5. import tensorflow as tf
  6. #os.environ["CUDA_VISIBLE_DEVICES"] = "0"
  7. from model import ParallelModelCheckpoint
  8. gpu_nums=2
  9. pretrained_weights='unet_membrane.hdf5'
  10. with tf.device('/cpu:0'):
  11. model = unet()
  12. if(pretrained_weights):
  13. model.load_weights(pretrained_weights)
  14. parallel_model = multi_gpu_model(model, gpus=gpu_nums)
  15. parallel_model.compile(optimizer = Adam(lr = 1e-4), loss = 'binary_crossentropy', metrics = ['accuracy'])
  16. #model.summary()
  17. model_checkpoint=ParallelModelCheckpoint(model,'unet_membrane.hdf5')
  18. data_gen_args = dict(rotation_range=0.2,
  19. width_shift_range=0.05,
  20. height_shift_range=0.05,
  21. shear_range=0.05,
  22. zoom_range=0.05,
  23. horizontal_flip=True,
  24. fill_mode='nearest')#数据增强时的变换方式的字典
  25. myGene = trainGenerator(2,'data/membrane/train','image','label',data_gen_args,save_to_dir = None)#得到一个生成器,以batch=2的速率无限生成增强后的数据
  26. #model_checkpoint = ModelCheckpoint('unet_membrane.hdf5', monitor='loss',verbose=1, save_best_only=True)
  27. parallel_model.fit_generator(myGene,steps_per_epoch=300,epochs=5,callbacks=[model_checkpoint])
  28. testGene = testGenerator("data/membrane/test")
  29. results = parallel_model.predict_generator(testGene,30,verbose=1)
  30. #上面的返回值是:预测值的 Numpy 数组。
  31. saveResult("data/membrane/test1",results)#保存结果

步骤一:

导入multi_gpu_model

from keras.utils import multi_gpu_model

步骤二:

在cpu的scope下实例化model,官网推荐这么写,但是也有人经常没有也可以的,至于没用的话是有些影响的,官方这么解释的:

Instantiate the base model (or "template" model). We recommend doing this with under a CPU device scope, so that the model's weights are hosted on CPU memory.  Otherwise they may end up hosted on a GPU, which would complicate weight sharing.

实例化基本模型(或“模板”模型)。 我们建议在CPU设备范围内执行此操作,以便模型的权重托管在CPU内存上。 否则它们可能最终托管在GPU上,这会使重量分享变得复杂。

  1. with tf.device('/cpu:0'):
  2. model = unet()

步骤三:

多GPU设置与编译

  1. parallel_model = multi_gpu_model(model, gpus=gpu_nums)
  2. parallel_model.compile(optimizer = Adam(lr = 1e-4), loss = 'binary_crossentropy', metrics = ['accuracy'])

步骤四:

自己定义检查点Checkpoint类(我写在了model.py文件里了,见下面),因为之前那个回调函数不能用在多GPU情况下,是因为这个时候保存权重要用模板model去保存,也就是原始的单个model的实例去保存:

  1. class ParallelModelCheckpoint(ModelCheckpoint):
  2. def __init__(self,model,filepath, monitor='loss', verbose=0,
  3. save_best_only=True, save_weights_only=False,
  4. mode='auto', period=1):
  5. self.single_model = model
  6. super(ParallelModelCheckpoint,self).__init__(filepath, monitor, verbose,save_best_only, save_weights_only,mode, period)
  7. def set_model(self, model):
  8. super(ParallelModelCheckpoint,self).set_model(self.single_model)

后面就是按照主文件来就行了。

 

贴出来model.py文件:

  1. # -*- coding: utf-8 -*-
  2. import numpy as np
  3. import os
  4. import skimage.io as io
  5. import skimage.transform as trans
  6. import numpy as np
  7. from keras.models import *
  8. from keras.layers import *
  9. from keras.optimizers import *
  10. from keras.callbacks import ModelCheckpoint, LearningRateScheduler
  11. #from keras import backend as keras
  12. class ParallelModelCheckpoint(ModelCheckpoint):
  13. def __init__(self,model,filepath, monitor='loss', verbose=0,
  14. save_best_only=True, save_weights_only=False,
  15. mode='auto', period=1):
  16. self.single_model = model
  17. super(ParallelModelCheckpoint,self).__init__(filepath, monitor, verbose,save_best_only, save_weights_only,mode, period)
  18. def set_model(self, model):
  19. super(ParallelModelCheckpoint,self).set_model(self.single_model)
  20. def unet(pretrained_weights = None,input_size = (256,256,1)):
  21. inputs1 = Input(input_size)
  22. conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs1)
  23. conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)
  24. pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
  25. conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)
  26. conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)
  27. pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
  28. conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)
  29. conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)
  30. pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)
  31. conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)
  32. conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)
  33. drop4 = Dropout(0.5)(conv4)
  34. pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)
  35. conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)
  36. conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)
  37. drop5 = Dropout(0.5)(conv5)
  38. up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5))#上采样之后再进行卷积,相当于转置卷积操作!
  39. merge6 = concatenate([drop4,up6],axis=3)
  40. conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)
  41. conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)
  42. up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6))
  43. merge7 = concatenate([conv3,up7],axis = 3)
  44. conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)
  45. conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)
  46. up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7))
  47. merge8 = concatenate([conv2,up8],axis = 3)
  48. conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)
  49. conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)
  50. up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))
  51. merge9 = concatenate([conv1,up9],axis = 3)
  52. conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)
  53. conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
  54. conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
  55. conv10 = Conv2D(1, 1, activation = 'sigmoid')(conv9)#我怀疑这个sigmoid激活函数是多余的,因为在后面的loss中用到的就是二进制交叉熵,包含了sigmoid
  56. model = Model(inputs = inputs1, outputs = conv10)
  57. return model

 

 

data.py:

 

  1. # -*- coding: utf-8 -*-
  2. from __future__ import print_function
  3. from keras.preprocessing.image import ImageDataGenerator
  4. import numpy as np
  5. import os
  6. import glob
  7. import skimage.io as io
  8. import skimage.transform as trans
  9. Sky = [128,128,128]
  10. Building = [128,0,0]
  11. Pole = [192,192,128]
  12. Road = [128,64,128]
  13. Pavement = [60,40,222]
  14. Tree = [128,128,0]
  15. SignSymbol = [192,128,128]
  16. Fence = [64,64,128]
  17. Car = [64,0,128]
  18. Pedestrian = [64,64,0]
  19. Bicyclist = [0,128,192]
  20. Unlabelled = [0,0,0]
  21. COLOR_DICT = np.array([Sky, Building, Pole, Road, Pavement,
  22. Tree, SignSymbol, Fence, Car, Pedestrian, Bicyclist, Unlabelled])
  23. def adjustData(img,mask,flag_multi_class,num_class):
  24. if(flag_multi_class):#此程序中不是多类情况,所以不考虑这个
  25. img = img / 255
  26. mask = mask[:,:,:,0] if(len(mask.shape) == 4) else mask[:,:,0]#if else的简洁写法,一行表达式,为真时放在前面
  27. new_mask = np.zeros(mask.shape + (num_class,))#np.zeros里面是shape元组,此目的是扩展维度到5维
  28. for i in range(num_class):
  29. #for one pixel in the image, find the class in mask and convert it into one-hot vector
  30. #index = np.where(mask == i)
  31. #index_mask = (index[0],index[1],index[2],np.zeros(len(index[0]),dtype = np.int64) + i) if (len(mask.shape) == 4) else (index[0],index[1],np.zeros(len(index[0]),dtype = np.int64) + i)
  32. #new_mask[index_mask] = 1
  33. new_mask[mask == i,i] = 1
  34. new_mask = np.reshape(new_mask,(new_mask.shape[0],new_mask.shape[1]*new_mask.shape[2],new_mask.shape[3])) if flag_multi_class else np.reshape(new_mask,(new_mask.shape[0]*new_mask.shape[1],new_mask.shape[2]))
  35. mask = new_mask
  36. elif(np.max(img) > 1):
  37. img = img / 255
  38. mask = mask /255
  39. mask[mask > 0.5] = 1
  40. mask[mask <= 0.5] = 0
  41. return (img,mask)
  42. def trainGenerator(batch_size,train_path,image_folder,mask_folder,aug_dict,image_color_mode = "grayscale",
  43. mask_color_mode = "grayscale",image_save_prefix = "image",mask_save_prefix = "mask",
  44. flag_multi_class = False,num_class = 2,save_to_dir = None,target_size = (256,256),seed = 1):
  45. '''
  46. can generate image and mask at the same time
  47. use the same seed for image_datagen and mask_datagen to ensure the transformation for image and mask is the same
  48. if you want to visualize the results of generator, set save_to_dir = "your path"
  49. '''
  50. image_datagen = ImageDataGenerator(**aug_dict)
  51. mask_datagen = ImageDataGenerator(**aug_dict)
  52. image_generator = image_datagen.flow_from_directory(#https://blog.csdn.net/nima1994/article/details/80626239
  53. train_path,#训练数据文件夹路径
  54. classes = [image_folder],#类别文件夹,对哪一个类进行增强
  55. class_mode = None,#不返回标签
  56. color_mode = image_color_mode,#灰度,单通道模式
  57. target_size = target_size,#转换后的目标图片大小
  58. batch_size = batch_size,#每次产生的(进行转换的)图片张数
  59. save_to_dir = save_to_dir,#保存的图片路径
  60. save_prefix = image_save_prefix,#生成图片的前缀,仅当提供save_to_dir时有效
  61. seed = seed)
  62. mask_generator = mask_datagen.flow_from_directory(
  63. train_path,
  64. classes = [mask_folder],
  65. class_mode = None,
  66. color_mode = mask_color_mode,
  67. target_size = target_size,
  68. batch_size = batch_size,
  69. save_to_dir = save_to_dir,
  70. save_prefix = mask_save_prefix,
  71. seed = seed)
  72. train_generator = zip(image_generator, mask_generator)#组合成一个生成器
  73. for (img,mask) in train_generator:#由于batch是2,所以一次返回两张,即img是一个2张灰度图片的数组,[2,256,256]
  74. img,mask = adjustData(img,mask,flag_multi_class,num_class)#返回的img依旧是[2,256,256]
  75. yield (img,mask)#每次分别产出两张图片和标签
  76. def testGenerator(test_path,num_image = 30,target_size = (256,256),flag_multi_class = False,as_gray = True):
  77. for i in range(num_image):
  78. img = io.imread(os.path.join(test_path,"%d.png"%i),as_gray = as_gray)
  79. img = img / 255
  80. img = trans.resize(img,target_size)
  81. img = np.reshape(img,img.shape+(1,)) if (not flag_multi_class) else img
  82. img = np.reshape(img,(1,)+img.shape)#将测试图片扩展一个维度,与训练时的输入[2,256,256]保持一致
  83. yield img
  84. def geneTrainNpy(image_path,mask_path,flag_multi_class = False,num_class = 2,image_prefix = "image",mask_prefix = "mask",image_as_gray = True,mask_as_gray = True):
  85. image_name_arr = glob.glob(os.path.join(image_path,"%s*.png"%image_prefix))#相当于文件搜索,搜索某路径下与字符匹配的文件https://blog.csdn.net/u010472607/article/details/76857493/
  86. image_arr = []
  87. mask_arr = []
  88. for index,item in enumerate(image_name_arr):#enumerate是枚举,输出[(0,item0),(1,item1),(2,item2)]
  89. img = io.imread(item,as_gray = image_as_gray)
  90. img = np.reshape(img,img.shape + (1,)) if image_as_gray else img
  91. mask = io.imread(item.replace(image_path,mask_path).replace(image_prefix,mask_prefix),as_gray = mask_as_gray)#重新在mask_path文件夹下搜索带有mask字符的图片(标签图片)
  92. mask = np.reshape(mask,mask.shape + (1,)) if mask_as_gray else mask
  93. img,mask = adjustData(img,mask,flag_multi_class,num_class)
  94. image_arr.append(img)
  95. mask_arr.append(mask)
  96. image_arr = np.array(image_arr)
  97. mask_arr = np.array(mask_arr)#转换成array
  98. return image_arr,mask_arr#该函数主要是分别在训练集文件夹在和标签文件加下搜索图片,然后扩展一个维度后以array的形式返回。
  99. def labelVisualize(num_class,color_dict,img):
  100. img = img[:,:,0] if len(img.shape) == 3 else img
  101. img_out = np.zeros(img.shape + (3,))#变成RGB空间,因为其他颜色只能再RGB空间才会显示
  102. for i in range(num_class):
  103. img_out[img == i,:] = color_dict[i]#为不同类别图上不同的颜色,color_dict[i]是与类别数有关的颜色,img_out[img == i,:]是img_out在img中等于i类的位置上的点
  104. return img_out / 255
  105. '''
  106. def saveResult(save_path,npyfile,flag_multi_class = False,num_class = 2):
  107. for i,item in enumerate(npyfile):
  108. img = labelVisualize(num_class,COLOR_DICT,item) if flag_multi_class else item[:,:,0]
  109. io.imsave(os.path.join(save_path,"%d_predict.png"%i),img)
  110. '''
  111. def saveResult(save_path,npyfile,flag_multi_class = False,num_class = 2):
  112. for i,item in enumerate(npyfile):
  113. if flag_multi_class:
  114. img = labelVisualize(num_class,COLOR_DICT,item)#多类的话就图成彩色,非多类(两类)的话就是黑白色
  115. else:
  116. img=item[:,:,0]
  117. print(np.max(img),np.min(img))
  118. img[img>0.5]=1
  119. img[img<=0.5]=0
  120. print(np.max(img),np.min(img))
  121. io.imsave(os.path.join(save_path,"%d_predict.png"%i),img)

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/53730
推荐阅读
相关标签
  

闽ICP备14008679号