• 第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()

     

  • 相关阅读:
    实现双向链表的增删查改
    智慧工地管理系统源码(电脑端+手机端+APP+SAAS云平台)
    计算机毕业设计ssm基于协同过滤推荐算法的新闻推荐系统43149系统+程序+源码+lw+远程部署
    Orange Pi i96 入手填坑问题总结
    我是怎么设计一个消息推送接口的?
    Spring Boot-2.3.7.RELEASE整合activiti-6.0示例步骤
    【计算机网络】OSI七层网络参考模型
    C/C++数据结构——树的同构(二叉树)
    Python第二次作业(4)【矩形面积与周长】
    kafka伪集群部署,使用zookeeper模式
  • 原文地址:https://blog.csdn.net/qq_60245590/article/details/133411047