参考:PyTorch 学习笔记(三):transforms的二十二个方法_TensorSense的博客-CSDN博客
transform = transforms.Compose([
transforms.Resize(100),
transforms.RandomVerticalFlip(),
transforms.RandomCrop(50),
transforms.RandomResizedCrop(150),
transforms.ColorJitter(brightness=0.5, contrast=0.5, hue=0.5),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
根据上表我们可以得出
transform.Resize() 为将给定图片重新设置尺寸
transform.RandomVerticalFlip() 对传入图像p=0.5的水平翻转,方便后期更好的识别效果
transforms.RandomCrop(50) 随即取一块50*50的图片区域,意义同上句
transforms.RandomResizedCrop(150) 随即取一块150*150的图片区域,该操作的含义在于:即使只是该物体的一部分,我们也认为这是该类物体
transforms.ColorJitter(brightness=0.5, contrast=0.5, hue=0.5)
transforms.ColorJitter 改变图像的属性:亮度(brightness)、对比度(contrast)、饱和度(saturation)和色调(hue)
transforms.ToTensor()
ToTensor()
将shape
为(H, W, C)
的nump.ndarray
或img
转为shape
为(C, H, W)
的tensor
,其将每一个数值归一化到[0,1]
,其归一化方法比较简单,直接除以255即可。
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 其作用就是先将输入归一化到(0,1)
,再使用公式"(x-mean)/std"
,将每个元素分布到(-1,1)
很多代码里面是这样的:torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
这一组值是怎么来的捏?
这一组值是从imagenet
训练集中抽样算出来的。
dataset_train = datasets.ImageFolder('train集位置', transform)
print(dataset_train.imgs) #输出经过transform处理的图片
print(dataset_train.class_to_idx) #输出此图片所在位置
dataset_test = datasets.ImageFolder('test集位置', transform)
print(dataset_test.class_to_idx)
train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE,
shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE,
shuffle=True)
dataset=torchvision.datasets.ImageFolder(
root, transform=None,
target_transform=None,
loader=,
is_valid_file=None)
#我的code
dataset_train = datasets.ImageFolder('\\train', transform)
参数详解:
root:图片存储的根目录,即各类别文件夹所在目录的上一级目录。
transform:对图片进行预处理的操作(函数),原始图片作为输入,返回一个转换后的图片。
target_transform:对图片类别进行预处理的操作,输入为 target,输出对其的转换。 如果不传该参数,即对 target 不做任何转换,返回的顺序索引 0,1, 2…
loader:表示数据集加载方式,通常默认加载方式即可。
is_valid_file:获取图像文件的路径并检查该文件是否为有效文件的函数(用于检查损坏文件)
返回的dataset都有以下两种属性:
self.class_to_idx:类别对应的索引,与不做任何转换返回的 target 对应
self.imgs:保存(img-path, class) tuple的 list
1.DataLoader完整参数
class torch.utils.data.DataLoader(
dataset,
batch_size=1,
shuffle=False,
sampler=None,
batch_sampler=None,
num_workers=0,
collate_fn=
pin_memory=False,
drop_last=False,
timeout=0,
worker_init_fn=None)
#我的code
train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE,
shuffle=True)
DataLoader在数据集上提供单进程或多进程的迭代器
--shuffle:设置为True的时候,每个epoch都会打乱数据集
# 定义网络
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
self.conv1 = nn.Conv2d(3, 32, 3)
self.max_pool1 = nn.MaxPool2d(2)
self.conv2 = nn.Conv2d(32, 64, 3)
self.max_pool2 = nn.MaxPool2d(2)
self.conv3 = nn.Conv2d(64, 64, 3)
self.conv4 = nn.Conv2d(64, 64, 3)
self.max_pool3 = nn.MaxPool2d(2)
self.conv5 = nn.Conv2d(64, 128, 3)
self.conv6 = nn.Conv2d(128, 128, 3)
self.max_pool4 = nn.MaxPool2d(2)
self.fc1 = nn.Linear(4608, 512)
self.fc2 = nn.Linear(512, 1)
def forward(self, x):
in_size = x.size(0)
x = self.conv1(x)
x = F.relu(x)
x = self.max_pool1(x)
x = self.conv2(x)
x = F.relu(x)
x = self.max_pool2(x)
x = self.conv3(x)
x = F.relu(x)
x = self.conv4(x)
x = F.relu(x)
x = self.max_pool3(x)
x = self.conv5(x)
x = F.relu(x)
x = self.conv6(x)
x = F.relu(x)
x = self.max_pool4(x)
# 展开
x = x.view(in_size, -1)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = torch.sigmoid(x)
return x
参考VGGNet论文,进行了最简单的复现
optimizer = optim.Adam(model.parameters(), lr=modellr) #定义优化器
def adjust_learning_rate(optimizer, epoch):
"""Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
modellrnew = modellr * (0.1 ** (epoch // 5)) #更新学习率
print("lr:", modellrnew)
for param_group in optimizer.param_groups:
param_group['lr'] = modellrnew #存储学习率
Adam算法的特点:
1、结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点;
2、对内存需求较小;
3、为不同的参数计算不同的自适应学习率;
4、也适用于大多非凸优化-适用于大数据集和高维空间。
# 定义训练过程
def train(model, device, train_loader, optimizer, epoch):
model.train()#模型训练
for batch_idx, (data, target) in enumerate(train_loader): #循环参数更新
data, target = data.to(device), target.to(device).float().unsqueeze(1)
optimizer.zero_grad()
output = model(data)
# print(output)
loss = F.binary_cross_entropy(output, target)
loss.backward()
optimizer.step()
if (batch_idx + 1) % 10 == 0: #每一个周期后输出训练结果
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, (batch_idx + 1) * len(data), len(train_loader.dataset),
100. * (batch_idx + 1) / len(train_loader), loss.item()))
PyTorch的反向传播(即tensor.backward())是通过autograd包来实现的,autograd包会根据tensor进行过的数学运算来自动计算其对应的梯度。
具体来说,torch.tensor是autograd包的基础类,如果你设置tensor的requires_grads为True,就会开始跟踪这个tensor上面的所有运算,如果你做完运算后使用tensor.backward(),所有的梯度就会自动运算,tensor的梯度将会累加到它的.grad属性里面去。
更具体地说,损失函数loss是由模型的所有权重w经过一系列运算得到的,若某个w的requires_grads为True,则w的所有上层参数(后面层的权重w)的.grad_fn属性中就保存了对应的运算,然后在使用loss.backward()后,会一层层的反向传播计算每个w的梯度值,并保存到该w的.grad属性中。
如果没有进行tensor.backward()的话,梯度值将会是None,因此loss.backward()要写在optimizer.step()之前。
优化器的作用就是针对计算得到的参数梯度对网络参数进行更新,所以要想使得优化器起作用,主要需要两个东西:
def val(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device).float().unsqueeze(1)
output = model(data)
# print(output)
test_loss += F.binary_cross_entropy(output, target, reduction='mean').item()
pred = torch.tensor([[1] if num[0] >= 0.5 else [0] for num in output]).to(device)
correct += pred.eq(target.long()).sum().item()
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
一顿操作猛如虎,一看结果0.5