|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+import torch
|
|
|
2
|
+import torchvision.models
|
|
|
3
|
+import sys
|
|
|
4
|
+import pandas as pd
|
|
|
5
|
+import os
|
|
|
6
|
+from datasetmaker import TrainDataClass, TestDataClass
|
|
|
7
|
+from torch.utils.data import Dataset, DataLoader
|
|
|
8
|
+from torchvision import transforms, datasets
|
|
|
9
|
+from torch import optim
|
|
|
10
|
+import torch.nn as nn
|
|
|
11
|
+import torch.nn.functional as F
|
|
|
12
|
+
|
|
|
13
|
+weight_dir_root = 'weights_backup'
|
|
|
14
|
+acc_dir_root = 'accs'
|
|
|
15
|
+
|
|
|
16
|
+
|
|
|
17
|
+'''
|
|
|
18
|
+filename:train_model_1.0.py
|
|
|
19
|
+create date: 11/17/2020 Tue
|
|
|
20
|
+Author: Jeong Geol Kim
|
|
|
21
|
+Contact: atarib4816@gmail.com
|
|
|
22
|
+Description: This .py file will train, save weight and save Results with parameters. parameters will input by .txt file.
|
|
|
23
|
+filename format: partofarchitecture_backbone_yymmdd.lcfg
|
|
|
24
|
+eg) shapesort_mobilenetv2_201118.txt
|
|
|
25
|
+'''
|
|
|
26
|
+
|
|
|
27
|
+
|
|
|
28
|
+def Read_Config(lcfg_name):
|
|
|
29
|
+ '''
|
|
|
30
|
+ Description: read options from .lcfg and initialize learning options.
|
|
|
31
|
+ .lcfg file has 10 pramas: {role} {bbname} {dsversion} {dsname} {epoch} {batchsize} {optmizer} {learningloss} {lossfunction} {saveperiod}
|
|
|
32
|
+ '''
|
|
|
33
|
+ N_OF_PARAMS = 10
|
|
|
34
|
+ dict_key = ['role', 'bbname', 'dsversion', 'dsname', 'epoch', 'batchsize', 'optimizer', 'learningloss', 'lossfunction', 'saveperiod']
|
|
|
35
|
+
|
|
|
36
|
+ f = open(lcfg_name, 'r')
|
|
|
37
|
+ p_string = f.readline()
|
|
|
38
|
+ f.close()
|
|
|
39
|
+ params = p_string.split()
|
|
|
40
|
+
|
|
|
41
|
+ if len(params) != N_OF_PARAMS:
|
|
|
42
|
+ print("Error: {} is broken. please check learning configures. Or you have to change constant N_OF_PARMAS.".format(lcfg_name))
|
|
|
43
|
+ return
|
|
|
44
|
+ else:
|
|
|
45
|
+ print("----Learning Options----")
|
|
|
46
|
+ print("Role of Classifier: {}".format(params[0]))
|
|
|
47
|
+ print("Backbone Name: {}".format(params[1]))
|
|
|
48
|
+ print("Dataset version: {}".format(params[2]))
|
|
|
49
|
+ print("Dataset target: {}".format(params[3]))
|
|
|
50
|
+ print("Number of iterations: {}".format(params[4]))
|
|
|
51
|
+ print("Images per Batch: {}".format(params[5]))
|
|
|
52
|
+ print("Optimizer Name: {}".format(params[6]))
|
|
|
53
|
+ print("learning loss: {}".format(params[7]))
|
|
|
54
|
+ print("Loss function Name: {}".format(params[8]))
|
|
|
55
|
+ print("Period of saving weight: {}".format(params[9]))
|
|
|
56
|
+ parmas_dictionary = dict(zip(dict_key, params))
|
|
|
57
|
+
|
|
|
58
|
+ return parmas_dictionary
|
|
|
59
|
+
|
|
|
60
|
+
|
|
|
61
|
+def Preprocess(params, lcfg_name):
|
|
|
62
|
+ dataset_root = os.path.join(os.getcwd(),'datasets',params['dsversion'],params['dsname'])
|
|
|
63
|
+ classes = tuple(os.listdir(os.path.join(dataset_root,'train')))
|
|
|
64
|
+
|
|
|
65
|
+ #NOTE: we don't have to transform because we already shrink raw images as same as MoblieNetV2's input size
|
|
|
66
|
+ tsfm=transforms.Compose([
|
|
|
67
|
+ transforms.ToTensor()
|
|
|
68
|
+ ])
|
|
|
69
|
+
|
|
|
70
|
+ train = TrainDataClass(dataset_root, tsfm)
|
|
|
71
|
+ trainloader = DataLoader(train, batch_size=int(params['batchsize']), shuffle=True)
|
|
|
72
|
+
|
|
|
73
|
+ test = TestDataClass(dataset_root, tsfm)
|
|
|
74
|
+ testloader = DataLoader(test, batch_size=int(params['batchsize']), shuffle=True)
|
|
|
75
|
+
|
|
|
76
|
+ return classes, trainloader, testloader
|
|
|
77
|
+
|
|
|
78
|
+
|
|
|
79
|
+
|
|
|
80
|
+
|
|
|
81
|
+def Model_Construct(params, lcfg_name):
|
|
|
82
|
+ if params['bbname'] == 'moblienetv2':
|
|
|
83
|
+ model = torch.hub.load('pytorch/vision', 'mobilenet_v2', pretrained=True)
|
|
|
84
|
+ return model
|
|
|
85
|
+ if params['bbname'] == 'resnet50':
|
|
|
86
|
+ model = torch.hub.load('pytorch/vision', 'resnet50', pretrained=True)
|
|
|
87
|
+ return model
|
|
|
88
|
+
|
|
|
89
|
+
|
|
|
90
|
+def Train(params, lcfg_name, model, trainLoader, testLoader, criterion, optimizer):
|
|
|
91
|
+ params = params
|
|
|
92
|
+ lcfg_name = lcfg_name
|
|
|
93
|
+ model = model
|
|
|
94
|
+ trainLoader = trainLoader
|
|
|
95
|
+ criterion = criterion
|
|
|
96
|
+ optimizer = optimizer
|
|
|
97
|
+ weight_dir_root = 'weights_backup'
|
|
|
98
|
+ acc_dir_root = 'accs'
|
|
|
99
|
+
|
|
|
100
|
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
|
|
101
|
+ #print(torch.cuda.device_count())
|
|
|
102
|
+ print(device)
|
|
|
103
|
+ model = model.to(device)
|
|
|
104
|
+
|
|
|
105
|
+ for epoch in range(int(params['epoch'])):
|
|
|
106
|
+ running_loss = 0.0
|
|
|
107
|
+ for i, data in enumerate(trainLoader, 0):
|
|
|
108
|
+ # [inputs, labels]의 목록인 data로부터 입력을 받은 후;
|
|
|
109
|
+ inputs, labels = data[0].to(device), data[1].to(device)
|
|
|
110
|
+
|
|
|
111
|
+ # 변화도(Gradient) 매개변수를 0으로 만들고
|
|
|
112
|
+ optimizer.zero_grad()
|
|
|
113
|
+
|
|
|
114
|
+ # 순전파 + 역전파 + 최적화를 한 후
|
|
|
115
|
+ outputs = model(inputs).to(device)
|
|
|
116
|
+ loss = criterion(outputs, labels)
|
|
|
117
|
+ loss.backward()
|
|
|
118
|
+ optimizer.step()
|
|
|
119
|
+
|
|
|
120
|
+ # 통계를 출력합니다.
|
|
|
121
|
+ running_loss += loss.item()
|
|
|
122
|
+ #print(i)
|
|
|
123
|
+ if i % 50 == 49: # print every 50 mini-batches
|
|
|
124
|
+ print('[%d, %5d] loss: %.3f' %
|
|
|
125
|
+ (epoch + 1, i + 1, running_loss / 2000))
|
|
|
126
|
+ running_loss = 0.0
|
|
|
127
|
+ correct = 0
|
|
|
128
|
+ total = 0
|
|
|
129
|
+ with torch.no_grad():
|
|
|
130
|
+ for data in testLoader:
|
|
|
131
|
+ inputs, labels = data[0].to(device), data[1].to(device)
|
|
|
132
|
+ outputs = model(inputs).to(device)
|
|
|
133
|
+ _, predicted = torch.max(outputs.data, 1)
|
|
|
134
|
+ total += labels.size(0)
|
|
|
135
|
+ correct += (predicted == labels).sum().item()
|
|
|
136
|
+ accuracy = (100 * correct / total)
|
|
|
137
|
+ print('Accuracy: {0}'.format(accuracy))
|
|
|
138
|
+
|
|
|
139
|
+ acc_name = os.path.join(os.getcwd(),acc_dir_root,lcfg_name + '.csv')
|
|
|
140
|
+ weight_name = os.path.join(os.getcwd(), weight_dir_root,lcfg_name,params['role']+'_'+str(epoch+1)+'.pth')
|
|
|
141
|
+
|
|
|
142
|
+ if epoch % int(params['saveperiod']) == (int(params['saveperiod']) - 1):
|
|
|
143
|
+ params['savedat'] = epoch + 1
|
|
|
144
|
+ params['accuracy'] = accuracy
|
|
|
145
|
+ cols = params.keys()
|
|
|
146
|
+ #NOTE: dictionary needs extend
|
|
|
147
|
+ if not os.path.isfile(acc_name):
|
|
|
148
|
+ df = pd.DataFrame(index = range(0), columns = cols)
|
|
|
149
|
+ row = list(params.values())
|
|
|
150
|
+
|
|
|
151
|
+ df = df.append(pd.Series(row, index=cols), ignore_index= True)
|
|
|
152
|
+ df.to_csv(acc_name)
|
|
|
153
|
+ else:
|
|
|
154
|
+ df = pd.read_csv(acc_name, index_col = 0)
|
|
|
155
|
+ row = list(params.values())
|
|
|
156
|
+ df = df.append(pd.Series(row, index=cols), ignore_index= True)
|
|
|
157
|
+ df.to_csv(acc_name)
|
|
|
158
|
+
|
|
|
159
|
+ if not os.path.isfile(weight_name):
|
|
|
160
|
+ torch.save(model.state_dict(),weight_name)
|
|
|
161
|
+
|
|
|
162
|
+
|
|
|
163
|
+
|
|
|
164
|
+ #if epoch % int(params['saveperiod']) == (int(params['saveperiod']) - 1):
|
|
|
165
|
+ # torch.save(model.state_dict(), os.path.join(os.getcwd(), weight_dir_root,lcfg_name))
|
|
|
166
|
+
|
|
|
167
|
+ print('Finished Training')
|
|
|
168
|
+ return model
|
|
|
169
|
+
|
|
|
170
|
+
|
|
|
171
|
+'''
|
|
|
172
|
+def Test(model, testLoader):
|
|
|
173
|
+ correct = 0
|
|
|
174
|
+ total = 0
|
|
|
175
|
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
|
|
176
|
+
|
|
|
177
|
+ with torch.no_grad():
|
|
|
178
|
+ for data in testLoader:
|
|
|
179
|
+ inputs, labels = data[0].to(device), data[1].to(device)
|
|
|
180
|
+ outputs = model(inputs).to(device)
|
|
|
181
|
+ _, predicted = torch.max(outputs.data, 1)
|
|
|
182
|
+ total += labels.size(0)
|
|
|
183
|
+ correct += (predicted == labels).sum().item()
|
|
|
184
|
+ accuracy = (100 * correct / total)
|
|
|
185
|
+ print('Accuracy: {}'.format(accuracy))
|
|
|
186
|
+ return accuracy
|
|
|
187
|
+'''
|
|
|
188
|
+
|
|
|
189
|
+
|
|
|
190
|
+
|
|
|
191
|
+def Save_Weight(params, lcfg_name):
|
|
|
192
|
+ DEFAULT_DIR = 'weights_backup'
|
|
|
193
|
+ pass
|
|
|
194
|
+
|
|
|
195
|
+
|
|
|
196
|
+def Save_Result(params, lcfg_name):
|
|
|
197
|
+ DEFAULT_DIR = 'acc_save'
|
|
|
198
|
+ result_file = os.path.join(os.getcwd(),DEFAULT_DIR,lcfg_name) + '.csv'
|
|
|
199
|
+
|
|
|
200
|
+ if os.path.isfile(result_file):
|
|
|
201
|
+ df = pd.read_csv(result_file)
|
|
|
202
|
+ else:
|
|
|
203
|
+ cols = ['backbone', 'dataset', 'epoch', 'batchsize', 'optimizer', 'learningloss', 'lossfunction', 'saveat', 'accuracy', 'precision', 'recall']
|
|
|
204
|
+ #row = []
|
|
|
205
|
+ df = pd.DataFrame(params, columns = cols)
|
|
|
206
|
+ print(df)
|
|
|
207
|
+ df.to_csv(result_file)
|
|
|
208
|
+
|
|
|
209
|
+ pass
|
|
|
210
|
+
|
|
|
211
|
+def Set_Optimizer(params, model):
|
|
|
212
|
+ if params['optimizer'] == 'RMSprop':
|
|
|
213
|
+ optimizer = optim.RMSprop(model.parameters(), lr = float(params['learningloss']), momentum=0.9)
|
|
|
214
|
+ return optimizer
|
|
|
215
|
+ elif params['optimizer'] == 'SGD':
|
|
|
216
|
+ optimizer = optim.SGD(model.parameters(), lr = float(params['learningloss']), momentum=0.9)
|
|
|
217
|
+ return optimizer
|
|
|
218
|
+ elif params['optimizer'] == 'Adam':
|
|
|
219
|
+ optimizer = optim.Adam(model.parameters(), lr = float(params['learningloss']))
|
|
|
220
|
+ return optimizer
|
|
|
221
|
+
|
|
|
222
|
+
|
|
|
223
|
+
|
|
|
224
|
+
|
|
|
225
|
+
|
|
|
226
|
+
|
|
|
227
|
+
|
|
|
228
|
+if __name__ == "__main__":
|
|
|
229
|
+ for root, dirs, files in os.walk('.', topdown = True):
|
|
|
230
|
+ for filename in files:
|
|
|
231
|
+ if filename.endswith(('lcfg')):
|
|
|
232
|
+ lcfg_path = os.path.join(root, filename)
|
|
|
233
|
+ lcfg_name = os.path.splitext(filename)[0]
|
|
|
234
|
+
|
|
|
235
|
+ params = Read_Config(lcfg_path)
|
|
|
236
|
+ #print(params)
|
|
|
237
|
+ if not os.path.exists(os.path.join(os.getcwd(),weight_dir_root,lcfg_name)):
|
|
|
238
|
+ os.makedirs(os.path.join(os.getcwd(),weight_dir_root,lcfg_name), exist_ok=True)
|
|
|
239
|
+ else:
|
|
|
240
|
+ pass
|
|
|
241
|
+
|
|
|
242
|
+ classes, trainLoader, testLoader = Preprocess(params, lcfg_name)
|
|
|
243
|
+ #print(classes)
|
|
|
244
|
+
|
|
|
245
|
+ model = Model_Construct(params, lcfg_name)
|
|
|
246
|
+
|
|
|
247
|
+ #print(model.eval())
|
|
|
248
|
+ if params['lossfunction'] == 'CrossEntropyLoss':
|
|
|
249
|
+ criterion = nn.CrossEntropyLoss()
|
|
|
250
|
+ else:
|
|
|
251
|
+ criterion = nn.MultiLabelMarginLoss()
|
|
|
252
|
+
|
|
|
253
|
+ optimizer = Set_Optimizer(params, model)
|
|
|
254
|
+
|
|
|
255
|
+ model = Train(params, lcfg_name, model, trainLoader, testLoader, criterion, optimizer)
|
|
|
256
|
+ else:
|
|
|
257
|
+ pass
|