News from this site

 Rental advertising space, please contact the webmaster if you need cooperation


+focus
focused

classification  

no classification

tag  

no tag

date  

no datas

Image classification tasks using Pytorch

posted on 2023-05-03 21:06     read(241)     comment(0)     like(15)     collect(3)


Overview:

This article will organize your own training data, use the Pytorch deep learning framework to train your own model, and finally realize your own image classification! This article takes the identification of balconies as an example to describe.

1. Data preparation

The basis of deep learning is data. To complete image classification, of course data is also essential. First use the crawler to crawl 1200 balcony pictures and 1200 non-balcony pictures. The names of the pictures are edited from 0.jpg to 2400.jpg, and the crawled pictures are placed in the same folder and named image (as shown in Figure 1 below Show).

figure 1

 The crawler code for Baidu pictures is also put here for your convenience. The code can crawl any custom pictures:

  1. import requests
  2. import os
  3. import urllib
  4. class Spider_baidu_image():
  5. def __init__(self):
  6. self.url = 'http://image.baidu.com/search/acjson?'
  7. self.headers = {
  8. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.\
  9. 3497.81 Safari/537.36'}
  10. self.headers_image = {
  11. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.\
  12. 3497.81 Safari/537.36',
  13. 'Referer': 'http://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&sf=1&fmq=1557124645631_R&pv=&ic=&nc=1&z=&hd=1&latest=0&copyright=0&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&sid=&word=%E8%83%A1%E6%AD%8C'}
  14. self.keyword = input("请输入搜索图片关键字:")
  15. self.paginator = int(input("请输入搜索页数,每页30张图片:"))
  16. def get_param(self):
  17. """
  18. 获取url请求的参数,存入列表并返回
  19. :return:
  20. """
  21. keyword = urllib.parse.quote(self.keyword)
  22. params = []
  23. for i in range(1, self.paginator + 1):
  24. params.append(
  25. 'tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord={}&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=1&latest=0&copyright=0&word={}&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&cg=star&pn={}&rn=30&gsm=78&1557125391211='.format(
  26. keyword, keyword, 30 * i))
  27. return params
  28. def get_urls(self, params):
  29. """
  30. 由url参数返回各个url拼接后的响应,存入列表并返回
  31. :return:
  32. """
  33. urls = []
  34. for i in params:
  35. urls.append(self.url + i)
  36. return urls
  37. def get_image_url(self, urls):
  38. image_url = []
  39. for url in urls:
  40. json_data = requests.get(url, headers=self.headers).json()
  41. json_data = json_data.get('data')
  42. for i in json_data:
  43. if i:
  44. image_url.append(i.get('thumbURL'))
  45. return image_url
  46. def get_image(self, image_url):
  47. """
  48. 根据图片url,在本地目录下新建一个以搜索关键字命名的文件夹,然后将每一个图片存入。
  49. :param image_url:
  50. :return:
  51. """
  52. cwd = os.getcwd()
  53. file_name = os.path.join(cwd, self.keyword)
  54. if not os.path.exists(self.keyword):
  55. os.mkdir(file_name)
  56. for index, url in enumerate(image_url, start=1):
  57. with open(file_name + '\\{}.jpg'.format(index), 'wb') as f:
  58. f.write(requests.get(url, headers=self.headers_image).content)
  59. if index != 0 and index % 30 == 0:
  60. print('{}第{}页下载完成'.format(self.keyword, index / 30))
  61. def __call__(self, *args, **kwargs):
  62. params = self.get_param()
  63. urls = self.get_urls(params)
  64. image_url = self.get_image_url(urls)
  65. self.get_image(image_url)
  66. if __name__ == '__main__':
  67. spider = Spider_baidu_image()
  68. spider()

Each picture needs to be tagged accordingly, so in the txt file, select the name of the picture and add a tag after it. The label is 1 if it is a balcony, and 0 if it is not a balcony. Among the 2400 pictures, it is divided into two txt documents as the training set and the verification set "train.txt" and "val.txt" (as shown in Figures 2 and 3 below)

figure 2

 

image 3

 

通过观察自己爬取的图片,可以发现阳台各式各样,有的半开放,有的是封闭式的,有的甚至和其他可识别物体花,草混在一起。同时,图片尺寸也不一致,有的是竖放的长方形,有的是横放的长方形,但我们最终需要是合理尺寸的正方形。所以我们使用Resize的库用于给图像进行缩放操作,我这里把图片缩放到84*84的级别。除缩放操作以外还需对数据进行预处理:

torchvision.transforms是pytorch中的图像预处理包

一般用Compose把多个步骤整合到一起:

比如说

transforms.Compose([

transforms.CenterCrop(84),

transforms.ToTensor(),

])

这样就把两个步骤整合到一起

CenterCrop用于从中心裁剪图片,目标是一个长宽都为84的正方形,方便后续的计算。除CenterCrop外补充一个RandomCrop是在一个随机的位置进行裁剪。

ToTenser()这个函数的目的就是读取图片像素并且转化为0-1的数字(进行归一化操作)。

代码如下:

  1. data_transforms = {
  2. 'train': transforms.Compose([
  3. transforms.Resize(84),
  4. transforms.CenterCrop(84),
  5. # 转换成tensor向量
  6. transforms.ToTensor(),
  7. # 对图像进行归一化操作
  8. # [0.485, 0.456, 0.406],RGB通道的均值与标准差
  9. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  10. ]),
  11. 'val': transforms.Compose([
  12. transforms.Resize(84),
  13. transforms.CenterCrop(84),
  14. transforms.ToTensor(),
  15. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  16. ]),
  17. }

解决对图像的处理过后,想要开始训练网络模型,首先要解决的就是图像数据的读入,Pytorch使用DataLoader来实现图像数据读入,代码如下:

  1. class my_Data_Set(nn.Module):
  2. def __init__(self, txt, transform=None, target_transform=None, loader=None):
  3. super(my_Data_Set, self).__init__()
  4. # 打开存储图像名与标签的txt文件
  5. fp = open(txt, 'r')
  6. images = []
  7. labels = []
  8. # 将图像名和图像标签对应存储起来
  9. for line in fp:
  10. line.strip('\n')
  11. line.rstrip()
  12. information = line.split()
  13. images.append(information[0])
  14. labels.append(int(information[1]))
  15. self.images = images
  16. self.labels = labels
  17. self.transform = transform
  18. self.target_transform = target_transform
  19. self.loader = loader
  20. # 重写这个函数用来进行图像数据的读取
  21. def __getitem__(self, item):
  22. # 获取图像名和标签
  23. imageName = self.images[item]
  24. label = self.labels[item]
  25. # 读入图像信息
  26. image = self.loader(imageName)
  27. # 处理图像数据
  28. if self.transform is not None:
  29. image = self.transform(image)
  30. return image, label
  31. # 重写这个函数,来看数据集中含有多少数据
  32. def __len__(self):
  33. return len(self.images)
  34. # 生成Pytorch所需的DataLoader数据输入格式
  35. train_dataset = my_Data_Set('train.txt', transform=data_transforms['train'], loader=Load_Image_Information)
  36. test_dataset = my_Data_Set('val.txt', transform=data_transforms['val'], loader=Load_Image_Information)
  37. train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True)
  38. test_loader = DataLoader(test_dataset, batch_size=10, shuffle=True)

可验证是否生成了DataLoader格式数据:

  1. # 验证是否生成DataLoader格式数据
  2. for data in train_loader:
  3. inputs, labels = data
  4. print(inputs)
  5. print(labels)
  6. for data in test_loader:
  7. inputs, labels = data
  8. print(inputs)
  9. print(labels)

二.定义一个卷积神经网络

卷积神经网络一种典型的多层神经网络,擅长处理图像特别是大图像的相关机器学习问题。卷积神经网络通过一系列的方法,成功地将大数据量的图像识别问题不断降维,最终使其能够被训练。卷积神经网络(CNN)最早由Yann LeCun提出并应用在手写体识别上。

一个典型的CNN网络架构如下图4:

图4

 首先导入Python需要的库:

  1. import torch
  2. from torch.utils.data import DataLoader
  3. from torchvision import transforms, datasets
  4. import torch.nn as nn
  5. import torch.nn.functional as F
  6. import torch.optim as optim
  7. from torch.autograd import Variable
  8. from torch.utils.data import Dataset
  9. import numpy as np
  10. import os
  11. from PIL import Image
  12. import warnings
  13. import matplotlib.pyplot as plt
  14. warnings.filterwarnings("ignore")
  15. plt.ion()

 定义一个卷积神经网络:

  1. class Net(nn.Module):
  2. def __init__(self):
  3. super(Net, self).__init__()
  4. self.conv1 = nn.Conv2d(3, 6, 5)
  5. self.pool = nn.MaxPool2d(2, 2)
  6. self.conv2 = nn.Conv2d(6, 16, 5)
  7. self.fc1 = nn.Linear(16 * 18 * 18, 800)
  8. self.fc2 = nn.Linear(800, 120)
  9. self.fc3 = nn.Linear(120, 10)
  10. def forward(self, x):
  11. x = self.pool(F.relu(self.conv1(x)))
  12. x = self.pool(F.relu(self.conv2(x)))
  13. x = x.view(-1, 16 * 18 * 18)
  14. x = F.relu(self.fc1(x))
  15. x = F.relu(self.fc2(x))
  16. x = self.fc3(x)
  17. return x
  18. net = Net()

我们首先定义了一个Net类,它封装了所以训练的步骤,包括卷积、池化、激活以及全连接操作。

__init__函数首先定义了所需要的所有函数,这些函数都会在forward中调用。从conv1说起,conv1实际上就是定义一个卷积层,3代表的是输入图像的像素数组的层数,一般来说就是输入的图像的通道数,比如这里使用的图像都是彩色图像,由R、G、B三个通道组成,所以数值为3;6代表的是我们希望进行6次卷积,每一次卷积都能生成不同的特征映射数组,用于提取图像的6种特征。每一个特征映射结果最终都会被堆叠在一起形成一个图像输出,再作为下一步的输入;5就是过滤框架的尺寸,表示我们希望用一个5 *5的矩阵去和图像中相同尺寸的矩阵进行点乘再相加,形成一个值。定义好了卷基层,我们接着定义池化层。池化层所做的事说来简单,其实就是因为大图片生成的像素矩阵实在太大了,我们需要用一个合理的方法在降维的同时又不失去物体特征,所以使用池化的技术,每四个元素合并成一个元素,用这一个元素去代表四个元素的值,所以图像体积会降为原来的四分之一。再往下一行,我们又一次碰见了一个卷基层:conv2,和conv1一样,它的输入也是一个多层像素数组,输出也是一个多层像素数组,不同的是这一次完成的计算量更大了,我们看这里面的参数分别是6,16,5。之所以为6是因为conv1的输出层数为6,所以这里输入的层数就是6;16代表conv2的输出层数,和conv1一样,16代表着这一次卷积操作将会学习图片的16种映射特征,特征越多理论上能学习的效果就越好。conv2使用的过滤框尺寸和conv1一样,所以不再重复。

For fc1, 16 is easy to understand, because the height of the image matrix generated by the last convolution is 16 layers. We cut the training image into a square size of 84 * 84, so the earliest input of the image is a 3 * 84 * 84 array. After the first 5*5 convolution, we can conclude that the result of the convolution is a 6*80*80 matrix, where 80 is because we use a 5*5 filter frame, when it is from the upper left After the first element of the corner is convolved, the center of the filter frame is from 2 to 78, not from 0 to 79, so the result is an 80*80 image. After a pooling layer, the width and height of the image size are reduced to 1/2 of the original size, so it becomes 40 * 40. Then another convolution was performed, and as last time, the length and width were reduced by 4 to become 36 * 36, and then the last layer of pooling was applied, and the final size was 18 * 18. So the size of the input data of the first fully connected layer is 16 * 18 * 18. What the three fully connected layers do is very similar, that is, continuous training, and finally output a binary classification value.

The forward function of the net class represents the entire process of forward calculation. forward accepts an input and returns a network output value, and the intermediate process is a process of calling the layer defined in the init function.

F.relu is an activation function that converts all non-zero values ​​into zero values. The last critical step in this image recognition is the real loop training operation.

  1. #训练
  2. cirterion = nn.CrossEntropyLoss()
  3. optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.5)
  4. for epoch in range(50):
  5. running_loss = 0.0
  6. for i, data in enumerate(train_loader, 0):
  7. inputs, labels = data
  8. inputs, labels = Variable(inputs), Variable(labels)
  9. optimizer.zero_grad() # 优化器清零
  10. outputs = net(inputs)
  11. loss = cirterion(outputs, labels)
  12. loss.backward()
  13. optimizer.step() #优化
  14. running_loss += loss.item()
  15. if i % 200 == 199:
  16. print('[%d %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 200))
  17. running_loss = 0.0
  18. print('finished training!')

Here we have conducted 50 trainings, and each training is to obtain the training data in the train_loader in batches, clear the gradient, calculate the output value, calculate the error, backpropagate and correct the model. We take the average error of every 200 calculations as observations. 

The following test phase:

  1. #测试
  2. correct = 0
  3. total = 0
  4. with torch.no_grad():
  5. for data in test_loader:
  6. images, labels = data
  7. outputs = net(Variable(images))
  8. _, predicted = torch.max(outputs.data, dim=1)
  9. total += labels.size(0)
  10. correct += (predicted == labels).sum()
  11. print('Accuracy of the network on the 400 test images: %d %%' % (100 * correct / total))

Finally, a recognition accuracy rate will be obtained.

3. The complete code is as follows:

  1. import torch
  2. from torch.utils.data import DataLoader
  3. from torchvision import transforms, datasets
  4. import torch.nn as nn
  5. import torch.nn.functional as F
  6. import torch.optim as optim
  7. from torch.autograd import Variable
  8. from torch.utils.data import Dataset
  9. import numpy as np
  10. import os
  11. from PIL import Image
  12. import warnings
  13. import matplotlib.pyplot as plt
  14. warnings.filterwarnings("ignore")
  15. plt.ion()
  16. data_transforms = {
  17. 'train': transforms.Compose([
  18. transforms.Resize(84),
  19. transforms.CenterCrop(84),
  20. # 转换成tensor向量
  21. transforms.ToTensor(),
  22. # 对图像进行归一化操作
  23. # [0.485, 0.456, 0.406],RGB通道的均值与标准差
  24. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  25. ]),
  26. 'val': transforms.Compose([
  27. transforms.Resize(84),
  28. transforms.CenterCrop(84),
  29. transforms.ToTensor(),
  30. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  31. ]),
  32. }
  33. def Load_Image_Information(path):
  34. # 图像存储路径
  35. image_Root_Dir= r'C:/Users/wbl/Desktop/pythonProject1/image/'
  36. # 获取图像的路径
  37. iamge_Dir = os.path.join(image_Root_Dir, path)
  38. # 以RGB格式打开图像
  39. # Pytorch DataLoader就是使用PIL所读取的图像格式
  40. return Image.open(iamge_Dir).convert('RGB')
  41. class my_Data_Set(nn.Module):
  42. def __init__(self, txt, transform=None, target_transform=None, loader=None):
  43. super(my_Data_Set, self).__init__()
  44. # 打开存储图像名与标签的txt文件
  45. fp = open(txt, 'r')
  46. images = []
  47. labels = []
  48. # 将图像名和图像标签对应存储起来
  49. for line in fp:
  50. line.strip('\n')
  51. line.rstrip()
  52. information = line.split()
  53. images.append(information[0])
  54. labels.append(int(information[1]))
  55. self.images = images
  56. self.labels = labels
  57. self.transform = transform
  58. self.target_transform = target_transform
  59. self.loader = loader
  60. # 重写这个函数用来进行图像数据的读取
  61. def __getitem__(self, item):
  62. # 获取图像名和标签
  63. imageName = self.images[item]
  64. label = self.labels[item]
  65. # 读入图像信息
  66. image = self.loader(imageName)
  67. # 处理图像数据
  68. if self.transform is not None:
  69. image = self.transform(image)
  70. return image, label
  71. # 重写这个函数,来看数据集中含有多少数据
  72. def __len__(self):
  73. return len(self.images)
  74. # 生成Pytorch所需的DataLoader数据输入格式
  75. train_dataset = my_Data_Set('train.txt', transform=data_transforms['train'], loader=Load_Image_Information)
  76. test_dataset = my_Data_Set('val.txt', transform=data_transforms['val'], loader=Load_Image_Information)
  77. train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True)
  78. test_loader = DataLoader(test_dataset, batch_size=10, shuffle=True)
  79. '''
  80. # 验证是否生成DataLoader格式数据
  81. for data in train_loader:
  82. inputs, labels = data
  83. print(inputs)
  84. print(labels)
  85. for data in test_loader:
  86. inputs, labels = data
  87. print(inputs)
  88. print(labels)
  89. '''
  90. class Net(nn.Module):
  91. def __init__(self):
  92. super(Net, self).__init__()
  93. self.conv1 = nn.Conv2d(3, 6, 5)
  94. self.pool = nn.MaxPool2d(2, 2)
  95. self.conv2 = nn.Conv2d(6, 16, 5)
  96. self.fc1 = nn.Linear(16 * 18 * 18, 800)
  97. self.fc2 = nn.Linear(800, 120)
  98. self.fc3 = nn.Linear(120, 10)
  99. def forward(self, x):
  100. x = self.pool(F.relu(self.conv1(x)))
  101. x = self.pool(F.relu(self.conv2(x)))
  102. x = x.view(-1, 16 * 18 * 18)
  103. x = F.relu(self.fc1(x))
  104. x = F.relu(self.fc2(x))
  105. x = self.fc3(x)
  106. return x
  107. net = Net()
  108. #训练
  109. cirterion = nn.CrossEntropyLoss()
  110. optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.5)
  111. for epoch in range(50):
  112. running_loss = 0.0
  113. for i, data in enumerate(train_loader, 0):
  114. inputs, labels = data
  115. inputs, labels = Variable(inputs), Variable(labels)
  116. optimizer.zero_grad() # 优化器清零
  117. outputs = net(inputs)
  118. loss = cirterion(outputs, labels)
  119. loss.backward()
  120. optimizer.step() #优化
  121. running_loss += loss.item()
  122. if i % 200 == 199:
  123. print('[%d %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 200))
  124. running_loss = 0.0
  125. print('finished training!')
  126. #测试
  127. correct = 0
  128. total = 0
  129. with torch.no_grad():
  130. for data in test_loader:
  131. images, labels = data
  132. outputs = net(Variable(images))
  133. _, predicted = torch.max(outputs.data, dim=1)
  134. total += labels.size(0)
  135. correct += (predicted == labels).sum()
  136. print('Accuracy of the network on the 400 test images: %d %%' % (100 * correct / total))

Welcome everyone to criticize and correct~



Category of website: technical article > Blog

Author:cindy

link:http://www.pythonblackhole.com/blog/article/274/ae7ac89a31c498a5b64b/

source:python black hole net

Please indicate the source for any form of reprinting. If any infringement is discovered, it will be held legally responsible.

15 0
collect article
collected

Comment content: (supports up to 255 characters)