cnn.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. # HAR CNN training
  2. import copy
  3. import time
  4. import numpy as np
  5. import pandas as pd
  6. from utils.utilities import *
  7. from sklearn.model_selection import train_test_split
  8. import torch
  9. import torch.nn as nn
  10. import struct
  11. from torch.optim import Adam
  12. import torch.utils.data as Data
  13. path_to_dataset = "../dataset/UCIHAR/"
  14. X_train, labels_train, list_ch_train = read_data(data_path=path_to_dataset, split="train") # Train
  15. X_test, labels_test, list_ch_test = read_data(data_path=path_to_dataset, split="test") # Test
  16. assert list_ch_train == list_ch_test, "Mistmatch in channels!"
  17. # Normalize
  18. X_train, X_test = standardize(X_train, X_test)
  19. X_train = torch.from_numpy(X_train).transpose(1, 2)
  20. X_test = torch.from_numpy(X_test).transpose(1, 2)
  21. # Train/Validation Split (If you don't want to split data, comment the following two lines, so as the variable "y_vld")
  22. # X_tr, X_vld, lab_tr, lab_vld = train_test_split(
  23. # X_train, labels_train, stratify=labels_train, random_state=123, test_size=0.15)
  24. # Hyperparameters
  25. batch_size = 450 # Batch size previously set to 600
  26. seq_len = 128 # Number of steps
  27. learning_rate = 0.001
  28. epochs = 100 # Previously set to 250
  29. n_classes = 8 # Number of classes
  30. n_channels = 9 # Number of files
  31. # One-hot encoding:
  32. y_tr = torch.from_numpy(labels_train - 1)
  33. y_test = torch.from_numpy(labels_test - 1)
  34. print(X_train.shape, y_tr.shape)
  35. # If you don't use the split, make sure to comment this line
  36. # y_vld = one_hot(lab_vld)
  37. har_train_tensor = Data.TensorDataset(X_train, y_tr)
  38. har_test_tensor = Data.TensorDataset(X_test, y_test)
  39. train_loader = Data.DataLoader(dataset=har_train_tensor,
  40. batch_size=128,
  41. shuffle=True,
  42. num_workers=0, )
  43. #设置一个测试集加载器
  44. test_loader = Data.DataLoader(dataset=har_test_tensor,
  45. batch_size=1,
  46. shuffle=True,
  47. num_workers=0, )
  48. class Net(nn.Module):
  49. def __init__(self):
  50. super(Net, self).__init__()
  51. #定义第一个卷积层
  52. self.conv1 = nn.Conv1d(in_channels=9,
  53. out_channels=18, #输出高度12
  54. kernel_size=2, #卷积核尺寸3*3
  55. stride=1,
  56. padding=1)
  57. self.conv2 = nn.Conv1d(in_channels=18,
  58. out_channels=36, #输出高度12
  59. kernel_size=2, #卷积核尺寸3*3
  60. stride=1,
  61. padding=1)
  62. self.conv3 = nn.Conv1d(in_channels=36,
  63. out_channels=72, #输出高度12
  64. kernel_size=2, #卷积核尺寸3*3
  65. stride=1,
  66. padding=1)
  67. self.conv4 = nn.Conv1d(in_channels=72,
  68. out_channels=144, #输出高度12
  69. kernel_size=2, #卷积核尺寸3*3
  70. stride=1,
  71. padding=1)
  72. self.linear = nn.Linear(1440, 8)
  73. self.maxpool1d = nn.MaxPool1d(kernel_size=2, stride=2, padding=1)
  74. self.dropout = nn.Dropout(p = 0.5)
  75. self.relu = nn.ReLU()
  76. #定义网络的前向传播路径
  77. def forward(self,x):
  78. x = self.relu(self.conv1(x))
  79. x = self.maxpool1d(x)
  80. x = self.relu(self.conv2(x))
  81. x = self.maxpool1d(x)
  82. x = self.relu(self.conv3(x))
  83. x = self.maxpool1d(x)
  84. x = self.relu(self.conv4(x))
  85. x = self.maxpool1d(x)
  86. x = x.view(x.shape[0],-1)
  87. x = self.dropout(x)
  88. output = nn.functional.softmax(self.linear(x))
  89. return output
  90. #输出网络结构
  91. net = Net() #创建实例
  92. def save(model, filename):
  93. def traverse(tensor, f):
  94. for i in tensor:
  95. if len(i.shape) == 0:
  96. f.write(struct.pack(">f", (float)(i.data)))
  97. else:
  98. traverse(i, f)
  99. f = open(filename, 'wb')
  100. for _,param in enumerate(model.named_parameters()):
  101. traverse(param[1], f)
  102. print(str(param[0]))
  103. save(net, 'cnn_model.bin')
  104. #定义网络的训练过程函数
  105. def train_model(model,traindataloader,train_rate,criterion,optimizer,num_epochs=50):
  106. #train_rate:训练集中训练数量的百分比
  107. #计算训练使用的batch数量
  108. batch_num = len(traindataloader)
  109. train_batch_num = round(batch_num * train_rate) #前train_rate(80%)的batch进行训练
  110. #复制最好模型的参数
  111. best_model_wts = copy.deepcopy(model.state_dict())
  112. best_acc = 0.0
  113. train_loss_all = []
  114. train_acc_all = []
  115. val_loss_all = []
  116. val_acc_all = []
  117. since = time.time()
  118. for epoch in range(num_epochs):
  119. print('Epoch {}/{}'.format(epoch,num_epochs-1)) #格式化字符串
  120. print('-' * 10)
  121. #每个epoch有两个训练阶段
  122. train_loss = 0.0
  123. train_corrects = 0
  124. train_num = 0
  125. val_loss = 0.0
  126. val_corrects = 0
  127. val_num = 0
  128. for step,(b_x,b_y) in enumerate(traindataloader,1): #取标签和样本
  129. b_x = b_x.float()
  130. b_y = b_y.long()
  131. if step < train_batch_num: #前train_rate(80%)的batch进行训练
  132. model.train() #设置模型为训练模式,对Droopou有用
  133. output = model(b_x)
  134. # print(b_x)#取得模型预测结果
  135. pre_lab = torch.argmax(output,1) #横向获得最大值位置
  136. loss = criterion(output, b_y) #每个样本的loss
  137. optimizer.zero_grad()
  138. loss.backward()
  139. optimizer.step() #修改权值
  140. train_loss += loss.item() * b_x.size(0)
  141. #print(pre_lab)
  142. #print(b_y.data)
  143. train_corrects += torch.sum(pre_lab == b_y.data) #训练正确个数
  144. train_num += b_x.size(0)
  145. else:
  146. model.eval() #设置模型为验证模式
  147. output = model(b_x)
  148. pre_lab = torch.argmax(output,1)
  149. loss = criterion(output,b_y)
  150. val_loss += loss.item() * b_x.size(0)
  151. val_corrects += torch.sum(pre_lab == b_y.data)
  152. val_num += b_x.size(0)
  153. #计算训练集和验证集上的损失和精度
  154. train_loss_all.append(train_loss / train_num) #一个epoch上的loss
  155. train_acc_all.append(train_corrects.double().item() / train_num)
  156. val_loss_all.append(val_loss / val_num)
  157. val_acc_all.append(val_corrects.double().item() / val_num)
  158. print('{} Train Loss: {:.4f} Train Acc: {:.4f}'.format(epoch,train_loss_all[-1],train_acc_all[-1])) #此处-1没搞明白
  159. print('{} Val Loss: {:.4f} Val Acc: {:.4f}'.format(epoch,val_loss_all[-1],val_acc_all[-1]))
  160. #拷贝模型最高精度下的参数
  161. if val_acc_all[-1] > best_acc:
  162. best_acc = val_acc_all[-1]
  163. best_model_wts = copy.deepcopy(model.state_dict())
  164. torch.save(model.state_dict(),"UCI_HAR_model")
  165. torch.save(optimizer.state_dict(),"UCI_HAR_optimizer")
  166. save(model, "cnn_model.bin")
  167. time_use = time.time() - since
  168. print("Train and val complete in {:.0f}m {:.0f}s".format(time_use // 60,time_use % 60)) #训练用时
  169. #使用最好模型的参数
  170. model.load_state_dict(best_model_wts)
  171. #组成数据表格train_process打印
  172. train_process = pd.DataFrame(data={"epoch":range(num_epochs),
  173. "train_loss_all":train_loss_all,
  174. "val_loss_all":val_loss_all,
  175. "train_acc_all":train_acc_all,
  176. "val_acc_all":val_acc_all})
  177. return model,train_process
  178. #对模型进行训练
  179. optimizer = Adam(net.parameters(),lr=0.0003) #优化器
  180. criterion = nn.CrossEntropyLoss() #使用交叉熵作为损失函数
  181. net,train_process = train_model(net,train_loader,0.8, #使用训练集的20%作为验证
  182. criterion,optimizer,num_epochs=100)
  183. #对测试集进行预测,计算模型的泛化能力
  184. def test(model,testdataloader,criterion):
  185. test_loss_all = []
  186. test_acc_all = []
  187. test_loss = 0.0
  188. test_corrects = 0
  189. test_num = 0
  190. for step,(input, target) in enumerate(testdataloader): #取标签和样本
  191. input = input.float()
  192. target = target.long()
  193. model.eval() #设置模型为训练模式,对Droopou有用
  194. output = model(input)
  195. # print(b_x)#取得模型预测结果
  196. pre_lab = torch.argmax(output,1) #横向获得最大值位置
  197. loss = criterion(output,target) #每个样本的loss
  198. test_loss += loss.item() * input.size(0) #此处的b_x.size(0)=batch_size。此处相当于一个batch的loss?计算的是整体训练的loss
  199. #print(pre_lab)
  200. #print(input.data)
  201. test_corrects += torch.sum(pre_lab == target.data) #测试正确个数
  202. test_num += input.size(0)
  203. test_loss_all.append(test_loss / test_num)
  204. test_acc_all.append(test_corrects.double().item() / test_num)
  205. print('Test all Loss: {:.4f} Test Acc: {:.4f}'.format(test_loss_all[-1], test_acc_all[-1]))
  206. test = test(net,test_loader,criterion)