SageMaker使用托管容器训练本地网络模型
SageMaker使用托管容器訓練本地網絡模型
1.實驗簡介
本實驗使用SageMaker的托管容器來訓練本地編寫好的代碼,并將代碼上傳到jupyter notebook中調用SageMaker中的API接口調用托管容器來進行模型的訓練。
1.1項目結構目錄
實驗代碼放在TensorFlow文件夾下面,其中models放的是神經網絡的model代碼,tools是編寫好的數據加載器。sagemaker.ipynp為調用SageMaker的容器API代碼(我將通過此來申請SageMaker中的容器來訓練模型)。
|—TensorFlow
? |—models
? |—tools
|—sagemaker.ipynp
1.2腳本模式
為了方便,這里使用的是SageMaker托管容器中的腳本模式,該模式使用十分便捷,只需要編寫好代碼和根據要求使用托管容器中的訓練環境變量來編寫腳本就行了。
1.3使用的深度學習框架
SageMaker支持眾多深度學習框架:Mxnet,Tensorflow,PyTorch(國內用的最多)等等。
本人使用的框架是tensorflow2.x版本,tensorflow2.x高度封裝,與tensorflow1.x相比更加便捷和強大。并且tensorflow2.x使用的是熱計算模式。
2.編寫經典深度學習模型VGG16
本模型以VGG16為baseline,VGG16有強大的擬合能力,為了防止過擬合,我分別使用了Dropout,BatchNormalization,regularizers策略,并且對數據集進行了數據增強。
import tensorflow.keras as keras from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten from tensorflow.keras.layers import Conv2D, MaxPooling2D, BatchNormalization from tensorflow.keras import regularizersdef vgg_16_optim_Sequential():weight_decay = 0.0005model = Sequential()model.add(Conv2D(64, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.3))model.add(Conv2D(64, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2, 2),strides=(2,2),padding='same') )model.add(Conv2D(128, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.4))model.add(Conv2D(128, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Conv2D(256, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.4))model.add(Conv2D(256, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.4))model.add(Conv2D(256, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Conv2D(512, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.4))model.add(Conv2D(512, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.4))model.add(Conv2D(512, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Conv2D(512, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.4))model.add(Conv2D(512, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.4))model.add(Conv2D(512, (3, 3), padding='same',kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Dropout(0.5))model.add(Flatten())model.add(Dense(512,kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.5))model.add(Dense(512,kernel_regularizer=regularizers.l2(weight_decay)))model.add(Activation('relu'))model.add(BatchNormalization())model.add(Dropout(0.5))model.add(Dense(3))model.add(Activation('softmax'))return model3.編寫數據加載器
這里使用的是tensorflow中的提供的TFRecord數據結構,對應的文件后綴為.tfrecords。TFRecord更提供高效的數據輸入流,我們將無需將整個數據集讀入到內存中就對數據集進行批量的操作。這里我提供了基于對圖片的色澤改變的數據在線增強功能。而圖片的旋轉和隨機縮放裁剪等增強功能使用的離線增強(不在展示代碼中)。
import tensorflow as tf import os import numpy as np import randomclass tfrec_pre:def __init__(self,rootpath,train_val_percentage=None,train_val_abs=None,resize=None):self.dataroot = rootpathself.tfrecord_train = os.path.join(rootpath , 'train.tfrecords')self.tfrecord_val = os.path.join(rootpath , 'val.tfrecords')self.train_val_percentage = train_val_percentageself.train_val_abs = train_val_absself.resize = resizeself.classlist = []# 定義Feature結構,告訴解碼器每個Feature的類型是什么self.feature_description = { 'image': tf.io.FixedLenFeature([], tf.string),'label': tf.io.FixedLenFeature([], tf.int64),}def _parse_example_jpeg(self,example_string):# 將 TFRecord 文件中的每一個序列化的 tf.train.Example 解碼feature_dict = tf.io.parse_single_example(example_string, self.feature_description)feature_dict['image'] = tf.io.decode_jpeg(feature_dict['image'],channels=3) # 解碼JPEG圖片return feature_dict['image'], feature_dict['label']def _parse_example_png(self,example_string):feature_dict = tf.io.parse_single_example(example_string, self.feature_description)feature_dict['image'] = tf.io.decode_png(feature_dict['image'],channels=3) # 解碼PNG圖片return feature_dict['image'], feature_dict['label']def _resize_img(self,img,label):#圖片歸一image_resized = tf.image.resize(img, self.resize) / 255.0return image_resized,labeldef _Data_Augmentation(self,image,label):#隨機水平翻轉圖像#image=tf.image.random_flip_left_right(img)#隨機改變圖像的亮度image=tf.image.random_brightness(image,0.1)#隨機改變對比度image=tf.image.random_contrast(image,0.9,1.1)#隨機改變飽和度image = tf.image.random_saturation(image,0.9,1.1)#隨機裁剪#image = tf.random_crop(image,[120,120,3])#隨機改變色調image = tf.image.random_hue(image,0.1)return image,label#只有train集def _tfrec_writer(self,train_filenames,train_labels):with tf.io.TFRecordWriter(self.tfrecord_train) as writer:for filename, label in zip(train_filenames, train_labels):image = open(filename, 'rb').read() # 讀取數據集圖片到內存,image 為一個 Byte 類型的字符串feature = { # 建立 tf.train.Feature 字典'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), # 圖片是一個 Bytes 對象'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label])) # 標簽是一個 Int 對象}example = tf.train.Example(features=tf.train.Features(feature=feature)) # 通過字典建立 Examplewriter.write(example.SerializeToString()) # 將Example序列化并寫入 TFRecord 文件#按絕對比例劃分(train,val) def _tfrec_writer_abs(self,train_filenames,train_labels,absper=None):dataset=list(zip(train_filenames,train_labels))random.shuffle(dataset) data_num = len(dataset) train_num = int(data_num * absper)with tf.io.TFRecordWriter(self.tfrecord_train) as train:print('開始寫入train集')for (filename,label) in dataset[:train_num-1]:image = open(filename, 'rb').read() # 讀取數據集圖片到內存,image 為一個 Byte 類型的字符串feature = { # 建立 tf.train.Feature 字典'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), # 圖片是一個 Bytes 對象'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label])) # 標簽是一個 Int 對象}example = tf.train.Example(features=tf.train.Features(feature=feature)) # 通過字典建立 Exampletrain.write(example.SerializeToString()) # 將Example序列化并寫入 TFRecord 文件with tf.io.TFRecordWriter(self.tfrecord_val) as val:print('開始寫入val集')for (filename,label) in dataset[train_num:]:image = open(filename, 'rb').read() # 讀取數據集圖片到內存,image 為一個 Byte 類型的字符串feature = { # 建立 tf.train.Feature 字典'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), # 圖片是一個 Bytes 對象'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label])) # 標簽是一個 Int 對象}example = tf.train.Example(features=tf.train.Features(feature=feature)) # 通過字典建立 Exampleval.write(example.SerializeToString()) # 將Example序列化并寫入 TFRecord 文件#按概率劃分(train,val) def _tfrec_writer_percentage(self,train_filenames,train_labels,percentage=None):with tf.io.TFRecordWriter(self.tfrecord_train) as train:with tf.io.TFRecordWriter(self.tfrecord_val) as val:#choices = np.random.choice([0, 1], size=1000, p=[percentage, 1-percentage])for (filename,label) in zip(train_filenames, train_labels):image = open(filename, 'rb').read() # 讀取數據集圖片到內存,image 為一個 Byte 類型的字符串feature = { # 建立 tf.train.Feature 字典'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), # 圖片是一個 Bytes 對象'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label])) # 標簽是一個 Int 對象}example = tf.train.Example(features=tf.train.Features(feature=feature)) # 通過字典建立 Examplechoice=np.random.choice([0, 1],p=[percentage, 1-percentage])if choice==0:train.write(example.SerializeToString()) # 將Example序列化并寫入 TFRecord 文件else:val.write(example.SerializeToString()) # 將Example序列化并寫入 TFRecord 文件def generate(self):train_filenames = []train_labels = []for root,dirs,files in os.walk(self.dataroot):for dirname in dirs:#將目錄名作為分類self.classlist.append(dirname)if os.path.split(root)[-1] in self.classlist:#獲取目錄名classname=os.path.split(root)[-1]new_filenames = [os.path.join(root,filename) for filename in files]train_filenames = train_filenames+new_filenames#找到目錄名對應的下標作為這類別的標簽train_labels = train_labels +[self.classlist.index(classname)] * len(new_filenames)if self.train_val_percentage == None and self.train_val_abs==None:self._tfrec_writer(train_filenames,train_labels)if self.train_val_percentage == None and self.train_val_abs!=None:self._tfrec_writer_abs(train_filenames,train_labels,self.train_val_abs)if self.train_val_percentage != None and self.train_val_abs==None:self._tfrec_writer_percentage(train_filenames,train_labels,self.train_val_percentage)if self.train_val_percentage != None and self.train_val_abs!=None:raise RuntimeError('不能同時使用參數train_val_abs和train_val_percentage')def load_tfrec_jpeg(self,filename):raw_dataset = tf.data.TFRecordDataset(os.path.join(self.dataroot,filename)) # 讀取 TFRecord 文件dataset = raw_dataset.map(self._parse_example_jpeg,num_parallel_calls=tf.data.experimental.AUTOTUNE)dataset = dataset.map(self._resize_img,num_parallel_calls=tf.data.experimental.AUTOTUNE)return datasetdef load_tfrec_png(self,filename):raw_dataset = tf.data.TFRecordDataset(os.path.join(self.dataroot,filename)) # 讀取 TFRecord 文件dataset = raw_dataset.map(self._parse_example_png)dataset = dataset.map(self._resize_img)return datasetdef load_tfrec_augdata(self,filename):raw_dataset = tf.data.TFRecordDataset(os.path.join(self.dataroot,filename)) # 讀取 TFRecord 文件dataset = raw_dataset.map(self._parse_example_png,num_parallel_calls=tf.data.experimental.AUTOTUNE)dataset = dataset.map(self._resize_img,num_parallel_calls=tf.data.experimental.AUTOTUNE)dataset = dataset.map(self._Data_Augmentation,num_parallel_calls=tf.data.experimental.AUTOTUNE)return dataset4.腳本代碼
這里我將展示腳本代碼代碼中的核心部分(使用容器中的訓練環境變量),其他關于模型調用和加載代碼這里將不在描述。
def parse_args():parser = argparse.ArgumentParser()# hyperparameters sent by the client are passed as command-line arguments to the scriptparser.add_argument('--epochs', type=int, default=5)parser.add_argument('--batch_size', type=int, default=32)parser.add_argument('--learning_rate', type=float, default=0.001)# data directoriesparser.add_argument('--train', type=str, default=os.environ.get('SM_CHANNEL_TRAIN'))parser.add_argument('--test', type=str, default=os.environ.get('SM_CHANNEL_TEST'))# model directory: we will use the default set by SageMaker, /opt/ml/modelparser.add_argument('--model_dir', type=str, default=os.environ.get('SM_MODEL_DIR')) return parser.parse_known_args()4.1使用訓練環境變量的原因
因為腳本模式是通過命令調用的形式進行代碼的調用的(python xxx.py --epochs 5 --batch_size 32 ·······),所以必須通過傳參來控制模型和代碼,而在sagemaker托管的容器中通過若干的訓練環境變量來描述模型的數據集路徑和模型輸出路徑。
打個比方:在模型訓練前,托管容器將從S3中下載數據集到容器中的指定目錄,我們的訓練模型將從指定的目錄中讀取數據,并且需要將訓練好的模型保存到指定目錄,當sagemaker銷毀容器時,指定的目錄將會被保留,并將指定目錄的數據上傳到S3中進行存儲的托管。
4.2相關的環境變量
重要的環境變量如下:
- SM_CHANNEL_TRAIN 數據集中訓練集的路徑
- SM_CHANNEL_TEST 數據集中測試集的路徑
- SM_MODEL_DIR 模型保存的路徑
···············
5.注意事項
因為使用的托管模式,而訓練模型的過程中的相關指標將不會打印到jupyter notebook的控制臺中,想要在jupyter 中看到輸出結果,請編寫TeosorBoard來捕獲。
6.sagemaker API的調用代碼
要調用sagemaker托管容器,需要呼叫相應的API接口來創建容器和開始訓練。
6.1導入sagemaker SDK庫
import sagemaker from sagemaker.tensorflow import TensorFlow import os6.2確定數據輸入路徑
bucket = 'your-bucketname' prefix = 'Tensorflow_demo'#S3的訓練數據路徑 train_path = 's3://{}/{}/data/train/train_AUG_ALL.tfrecords'.format(bucket,prefix) test_path = 's3://{}/{}/data/test/test_AUG_ALL.tfrecords'.format(bucket,prefix) #數據數據以鍵值對形式 inputs = {'train':train_path,'test': test_path} print(inputs)6.3調用API來呼叫容器來托管訓練
一些重要參數如下:
- entry_point 運行的腳本代碼(必)
- source_dir 項目文件夾(必)
- train_instance_type 計算實例類型(必)
- train_instance_count 實例數量(Tensorflow支持分布式訓練,要看你的訓練代碼是否支持分布(必))
- role role角色(必)
- base_job_name 任務名
- framework_version 框架版本(必)
- py_version python版本號(必)
- script_mode 是否啟用腳本模式
6.4啟動容器訓練
estimator.fit(inputs)7.獲取模型結果
去S3那里,那里會以名sagemaker-region-1-ID創建一個新的存儲桶并將模型保存到里面以base_job_name命名的文件夾中
總結
以上是生活随笔為你收集整理的SageMaker使用托管容器训练本地网络模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 将台式机装入笔记本是什么体验笔记本当台式
- 下一篇: 如何恢复Mac的Recovery分区苹果