• 第P7周—咖啡豆识别(1)


     数据集及wen件目录介绍:

    数据集:工作台 - Heywhale.com

    一、前期工作

    1.1 数据详情

    1. import torch
    2. import torch.nn as nn
    3. import torchvision.transforms as transforms
    4. import torchvision
    5. from torchvision import transforms, datasets
    6. import os,PIL,pathlib,warnings
    7. warnings.filterwarnings("ignore") #忽略警告信息
    8. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    9. print(device)
    10. import os,PIL,random,pathlib
    11. data_dir = 'D:/P7/49-data/'
    12. data_dir = pathlib.Path(data_dir)
    13. data_paths = list(data_dir.glob('*'))
    14. classeNames = [str(path).split("\\")[3] for path in data_paths]
    15. print(classeNames)
    16. image_count = len(list(data_dir.glob('*/*.png')))
    17. print("图片总数为:",image_count)

    1.2 图片预处理: 

    详见:写文章-CSDN创作中心icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/133411331

    1. train_transforms = transforms.Compose([
    2. transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸
    3. # transforms.RandomHorizontalFlip(), # 随机水平翻转
    4. transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    5. transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
    6. mean=[0.485, 0.456, 0.406],
    7. std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    8. ])
    9. test_transform = transforms.Compose([
    10. transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸
    11. transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    12. transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
    13. mean=[0.485, 0.456, 0.406],
    14. std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    15. ])
    16. total_data = datasets.ImageFolder("D:/P7/49-data",transform=train_transforms)
    17. print(total_data)

     标签量化处理:

    print(total_data.class_to_idx)

    1.4 划分数据集

    1. train_size = int(0.8 * len(total_data))
    2. test_size = len(total_data) - train_size
    3. train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
    4. print(train_dataset, test_dataset)

    二、搭建VGG—16模型 

    2.1 搭建VGG模型

    1. import torch.nn.functional as F
    2. class vgg16(nn.Module):
    3. def __init__(self):
    4. super(vgg16, self).__init__()
    5. # 卷积块1
    6. self.block1 = nn.Sequential(
    7. nn.Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    8. nn.ReLU(),
    9. nn.Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    10. nn.ReLU(),
    11. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    12. )
    13. # 卷积块2
    14. self.block2 = nn.Sequential(
    15. nn.Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    16. nn.ReLU(),
    17. nn.Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    18. nn.ReLU(),
    19. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    20. )
    21. # 卷积块3
    22. self.block3 = nn.Sequential(
    23. nn.Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    24. nn.ReLU(),
    25. nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    26. nn.ReLU(),
    27. nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    28. nn.ReLU(),
    29. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    30. )
    31. # 卷积块4
    32. self.block4 = nn.Sequential(
    33. nn.Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    34. nn.ReLU(),
    35. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    36. nn.ReLU(),
    37. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    38. nn.ReLU(),
    39. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    40. )
    41. # 卷积块5
    42. self.block5 = nn.Sequential(
    43. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    44. nn.ReLU(),
    45. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    46. nn.ReLU(),
    47. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    48. nn.ReLU(),
    49. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    50. )
    51. # 全连接网络层,用于分类
    52. self.classifier = nn.Sequential(
    53. nn.Linear(in_features=512*7*7, out_features=4096),
    54. nn.ReLU(),
    55. nn.Linear(in_features=4096, out_features=4096),
    56. nn.ReLU(),
    57. nn.Linear(in_features=4096, out_features=4)
    58. )
    59. def forward(self, x):
    60. x = self.block1(x)
    61. x = self.block2(x)
    62. x = self.block3(x)
    63. x = self.block4(x)
    64. x = self.block5(x)
    65. x = torch.flatten(x, start_dim=1)
    66. x = self.classifier(x)
    67. return x
    68. device = "cuda" if torch.cuda.is_available() else "cpu"
    69. print("Using {} device".format(device))
    70. model = vgg16().to(device)
    71. print(model)

                    

    2.2 模型信息 

     

    三、模型训练

    3.1 编写训练函数

    1. # 训练循环
    2. def train(dataloader, model, loss_fn, optimizer):
    3. size = len(dataloader.dataset) # 训练集的大小
    4. num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整)
    5. train_loss, train_acc = 0, 0 # 初始化训练损失和正确率
    6. for X, y in dataloader: # 获取图片及其标签
    7. X, y = X.to(device), y.to(device)
    8. # 计算预测误差
    9. pred = model(X) # 网络输出
    10. loss = loss_fn(pred, y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
    11. # 反向传播
    12. optimizer.zero_grad() # grad属性归零
    13. loss.backward() # 反向传播
    14. optimizer.step() # 每一步自动更新
    15. # 记录acc与loss
    16. train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
    17. train_loss += loss.item()
    18. train_acc /= size
    19. train_loss /= num_batches
    20. return train_acc, train_loss

    3.2  编写测试函数

    1. def test (dataloader, model, loss_fn):
    2. size = len(dataloader.dataset) # 测试集的大小
    3. num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整)
    4. test_loss, test_acc = 0, 0
    5. # 当不进行训练时,停止梯度更新,节省计算内存消耗
    6. with torch.no_grad():
    7. for imgs, target in dataloader:
    8. imgs, target = imgs.to(device), target.to(device)
    9. # 计算loss
    10. target_pred = model(imgs)
    11. loss = loss_fn(target_pred, target)
    12. test_loss += loss.item()
    13. test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()
    14. test_acc /= size
    15. test_loss /= num_batches
    16. return test_acc, test_loss

    3.3 模型训练 (完整代码)

    1. import torch
    2. import torch.nn as nn
    3. import torchvision.transforms as transforms
    4. from torchvision import datasets
    5. import pathlib
    6. import warnings
    7. warnings.filterwarnings("ignore")
    8. def main():
    9. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    10. print(device)
    11. data_dir = 'D:/P7/49-data/'
    12. data_dir = pathlib.Path(data_dir)
    13. data_paths = list(data_dir.glob('*'))
    14. classeNames = [str(path).split("\\")[3] for path in data_paths]
    15. print(classeNames)
    16. image_count = len(list(data_dir.glob('*/*.png')))
    17. print("图片总数为:", image_count)
    18. train_transforms = transforms.Compose([
    19. transforms.Resize([224, 224]),
    20. transforms.ToTensor(),
    21. transforms.Normalize(
    22. mean=[0.485, 0.456, 0.406],
    23. std=[0.229, 0.224, 0.225])
    24. ])
    25. test_transform = transforms.Compose([
    26. transforms.Resize([224, 224]),
    27. transforms.ToTensor(),
    28. transforms.Normalize(
    29. mean=[0.485, 0.456, 0.406],
    30. std=[0.229, 0.224, 0.225])
    31. ])
    32. total_data = datasets.ImageFolder("D:/P7/49-data", transform=train_transforms)
    33. print(total_data)
    34. train_size = int(0.8 * len(total_data))
    35. test_size = len(total_data) - train_size
    36. train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
    37. print(train_dataset, test_dataset)
    38. batch_size = 32
    39. train_dl = torch.utils.data.DataLoader(train_dataset,
    40. batch_size=batch_size,
    41. shuffle=True,
    42. num_workers=1)
    43. test_dl = torch.utils.data.DataLoader(test_dataset,
    44. batch_size=batch_size,
    45. shuffle=True,
    46. num_workers=1)
    47. class VGG16Lite(nn.Module):
    48. def __init__(self, num_classes=4):
    49. super(VGG16Lite, self).__init__()
    50. self.features = nn.Sequential(
    51. nn.Conv2d(3, 64, kernel_size=3, padding=1),
    52. nn.ReLU(inplace=True),
    53. nn.MaxPool2d(kernel_size=2, stride=2),
    54. nn.Conv2d(64, 128, kernel_size=3, padding=1),
    55. nn.ReLU(inplace=True),
    56. nn.MaxPool2d(kernel_size=2, stride=2),
    57. nn.Conv2d(128, 256, kernel_size=3, padding=1),
    58. nn.ReLU(inplace=True),
    59. nn.Conv2d(256, 256, kernel_size=3, padding=1),
    60. nn.ReLU(inplace=True),
    61. nn.MaxPool2d(kernel_size=2, stride=2),
    62. )
    63. self.classifier = nn.Sequential(
    64. nn.Linear(256 * 28 * 28, 4096),
    65. nn.ReLU(inplace=True),
    66. nn.Dropout(),
    67. nn.Linear(4096, 4096),
    68. nn.ReLU(inplace=True),
    69. nn.Dropout(),
    70. nn.Linear(4096, num_classes),
    71. )
    72. def forward(self, x):
    73. x = self.features(x)
    74. x = x.view(x.size(0), -1)
    75. x = self.classifier(x)
    76. return x
    77. device = "cuda" if torch.cuda.is_available() else "cpu"
    78. print("Using {} device".format(device))
    79. model = VGG16Lite().to(device)
    80. print(model)
    81. import torch.optim as optim
    82. optimizer = optim.Adam(model.parameters(), lr=1e-4)
    83. loss_fn = nn.CrossEntropyLoss()
    84. epochs = 40
    85. train_loss = []
    86. train_acc = []
    87. test_loss = []
    88. test_acc = []
    89. best_acc = 0
    90. best_model = None
    91. for epoch in range(epochs):
    92. model.train()
    93. epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    94. model.eval()
    95. epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    96. if epoch_test_acc > best_acc:
    97. best_acc = epoch_test_acc
    98. best_model = model.state_dict()
    99. train_acc.append(epoch_train_acc)
    100. train_loss.append(epoch_train_loss)
    101. test_acc.append(epoch_test_acc)
    102. test_loss.append(epoch_test_loss)
    103. lr = optimizer.param_groups[0]['lr']
    104. template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')
    105. print(template.format(epoch + 1, epoch_train_acc * 100, epoch_train_loss,
    106. epoch_test_acc * 100, epoch_test_loss, lr))
    107. PATH = './best_model_lite.pth'
    108. torch.save(best_model, PATH)
    109. print('Done')
    110. if __name__ == '__main__':
    111. main()

     3.4 轻量化上述VGG16模型

    1. import torch
    2. import torch.nn as nn
    3. import torchvision.transforms as transforms
    4. import torchvision
    5. from torchvision import transforms, datasets
    6. import os,PIL,pathlib,warnings
    7. warnings.filterwarnings("ignore") #忽略警告信息
    8. def main():
    9. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    10. print(device)
    11. import os,PIL,random,pathlib
    12. data_dir = 'D:/P7/49-data/'
    13. data_dir = pathlib.Path(data_dir)
    14. data_paths = list(data_dir.glob('*'))
    15. classeNames = [str(path).split("\\")[3] for path in data_paths]
    16. print(classeNames)
    17. image_count = len(list(data_dir.glob('*/*.png')))
    18. print("图片总数为:",image_count)
    19. train_transforms = transforms.Compose([
    20. transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸
    21. # transforms.RandomHorizontalFlip(), # 随机水平翻转
    22. transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    23. transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
    24. mean=[0.485, 0.456, 0.406],
    25. std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    26. ])
    27. test_transform = transforms.Compose([
    28. transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸
    29. transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    30. transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
    31. mean=[0.485, 0.456, 0.406],
    32. std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    33. ])
    34. total_data = datasets.ImageFolder("D:/P7/49-data",transform=train_transforms)
    35. print(total_data)
    36. print(total_data.class_to_idx)
    37. train_size = int(0.8 * len(total_data))
    38. test_size = len(total_data) - train_size
    39. train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
    40. print(train_dataset, test_dataset)
    41. batch_size = 32
    42. train_dl = torch.utils.data.DataLoader(train_dataset,
    43. batch_size=batch_size,
    44. shuffle=True,
    45. num_workers=1)
    46. test_dl = torch.utils.data.DataLoader(test_dataset,
    47. batch_size=batch_size,
    48. shuffle=True,
    49. num_workers=1)
    50. import torch.nn.functional as F
    51. class vgg16(nn.Module):
    52. def __init__(self):
    53. super(vgg16, self).__init__()
    54. # 卷积块1
    55. self.block1 = nn.Sequential(
    56. nn.Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    57. nn.ReLU(),
    58. nn.Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    59. nn.ReLU(),
    60. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    61. )
    62. # 卷积块2
    63. self.block2 = nn.Sequential(
    64. nn.Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    65. nn.ReLU(),
    66. nn.Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    67. nn.ReLU(),
    68. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    69. )
    70. # 卷积块3
    71. self.block3 = nn.Sequential(
    72. nn.Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    73. nn.ReLU(),
    74. nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    75. nn.ReLU(),
    76. nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    77. nn.ReLU(),
    78. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    79. )
    80. # 卷积块4
    81. self.block4 = nn.Sequential(
    82. nn.Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    83. nn.ReLU(),
    84. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    85. nn.ReLU(),
    86. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    87. nn.ReLU(),
    88. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    89. )
    90. # 卷积块5
    91. self.block5 = nn.Sequential(
    92. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    93. nn.ReLU(),
    94. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    95. nn.ReLU(),
    96. nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
    97. nn.ReLU(),
    98. nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))
    99. )
    100. # 全连接网络层,用于分类
    101. self.classifier = nn.Sequential(
    102. nn.Linear(in_features=512*7*7, out_features=4096),
    103. nn.ReLU(),
    104. nn.Linear(in_features=4096, out_features=4096),
    105. nn.ReLU(),
    106. nn.Linear(in_features=4096, out_features=4)
    107. )
    108. def forward(self, x):
    109. x = self.block1(x)
    110. x = self.block2(x)
    111. x = self.block3(x)
    112. x = self.block4(x)
    113. x = self.block5(x)
    114. x = torch.flatten(x, start_dim=1)
    115. x = self.classifier(x)
    116. return x
    117. device = "cuda" if torch.cuda.is_available() else "cpu"
    118. print("Using {} device".format(device))
    119. model = vgg16().to(device)
    120. print(model)
    121. # 统计模型参数量以及其他指标
    122. import torchsummary as summary
    123. summary.summary(model, (3, 224, 224))
    124. # 训练循环
    125. def train(dataloader, model, loss_fn, optimizer):
    126. size = len(dataloader.dataset) # 训练集的大小
    127. num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整)
    128. train_loss, train_acc = 0, 0 # 初始化训练损失和正确率
    129. for X, y in dataloader: # 获取图片及其标签
    130. X, y = X.to(device), y.to(device)
    131. # 计算预测误差
    132. pred = model(X) # 网络输出
    133. loss = loss_fn(pred, y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
    134. # 反向传播
    135. optimizer.zero_grad() # grad属性归零
    136. loss.backward() # 反向传播
    137. optimizer.step() # 每一步自动更新
    138. # 记录acc与loss
    139. train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
    140. train_loss += loss.item()
    141. train_acc /= size
    142. train_loss /= num_batches
    143. return train_acc, train_loss
    144. def test (dataloader, model, loss_fn):
    145. size = len(dataloader.dataset) # 测试集的大小
    146. num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整)
    147. test_loss, test_acc = 0, 0
    148. # 当不进行训练时,停止梯度更新,节省计算内存消耗
    149. with torch.no_grad():
    150. for imgs, target in dataloader:
    151. imgs, target = imgs.to(device), target.to(device)
    152. # 计算loss
    153. target_pred = model(imgs)
    154. loss = loss_fn(target_pred, target)
    155. test_loss += loss.item()
    156. test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()
    157. test_acc /= size
    158. test_loss /= num_batches
    159. return test_acc, test_loss
    160. import copy
    161. optimizer = torch.optim.Adam(model.parameters(), lr= 1e-4)
    162. loss_fn = nn.CrossEntropyLoss() # 创建损失函数
    163. epochs = 40
    164. train_loss = []
    165. train_acc = []
    166. test_loss = []
    167. test_acc = []
    168. best_acc = 0 # 设置一个最佳准确率,作为最佳模型的判别指标
    169. for epoch in range(epochs):
    170. model.train()
    171. epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    172. model.eval()
    173. epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    174. # 保存最佳模型到 best_model
    175. if epoch_test_acc > best_acc:
    176. best_acc = epoch_test_acc
    177. best_model = copy.deepcopy(model)
    178. train_acc.append(epoch_train_acc)
    179. train_loss.append(epoch_train_loss)
    180. test_acc.append(epoch_test_acc)
    181. test_loss.append(epoch_test_loss)
    182. # 获取当前的学习率
    183. lr = optimizer.state_dict()['param_groups'][0]['lr']
    184. template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')
    185. print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss,
    186. epoch_test_acc*100, epoch_test_loss, lr))
    187. # 保存最佳模型到文件中
    188. PATH = './best_model.pth' # 保存的参数文件名
    189. torch.save(model.state_dict(), PATH)
    190. print('Done')
    191. if __name__ == '__main__':
    192. main()

     

  • 相关阅读:
    R包的尽头是 C/C++
    数据赋能(121)——体系:数据清洗——实施过程、应用特点
    合肥工业大学计算机网络课设-在线留言板
    多端统一开发框架Taro、UniApp和WeApp这三个应用各自在前端开发领域有着独特的定位和功能
    Node.js 应用开发详解14 工具应用:使用 clinicj 工具实现通用性安全检查
    轻量封装WebGPU渲染系统示例<1>-彩色三角形(源码)
    第6章 Elasticsearch,分布式搜索引擎
    【迁移ORACLE数据到MogDB/openGauss时的字符集问题】
    树莓派 RaspBerryPi 网络配置相关与改造usb网络摄像头
    基于ssm+vue的美食分享网站
  • 原文地址:https://blog.csdn.net/qq_60245590/article/details/133411047