准确率 95.31%
几个关键点:
1、改模型:原始的resnet18首层使用的7x7的卷积核,CIFAR10图片太小不适合,要改成3x3的,步长和padding都要一并改成1。因为图太小,最大池化层也同样没用,删掉。最后一个全连接层输出改成10。
2、图片增强不要太多,只要训练集和验证集结果没有出现10%以上的差距都算不上过拟合。
3、学习率从0.1开始,10个epoch跑完loss值没有下降的话衰减50%
4、损失函数用CrossEntropyLoss
5、优化器用SGD
改模型代码:
# 定义模型
model_ft = torchvision.models.resnet18(pretrained=False)
# 修改模型
model_ft.conv1 = nn.Conv2d(3, 64, 3, stride=1, padding=1, bias=False) # 首层改成3x3卷积核
model_ft.maxpool = nn.MaxPool2d(1, 1, 0) # 图像太小 本来就没什么特征 所以这里通过1x1的池化核让池化层失效
num_ftrs = model_ft.fc.in_features # 获取(fc)层的输入的特征数
model_ft.fc = nn.Linear(num_ftrs, 10)
这里的最大池化层实在是想不出什么好办法直接删掉,只能用这个办法让其失效
如果不想用原始的模型也能自己写个
下面是我是随便写的一个
# 实现Resnet18
"""
ResNet18 是由17个卷积层和1个全连接层组成
下采样层的1x1卷积不算
池化不算 激活不算
主要思想是一个基础层 然后反复的重复这个基础层
"""
import torch
from torch import nn
# 基础块
from torch.nn import Conv2d, BatchNorm2d, ReLU, MaxPool2d, AdaptiveAvgPool2d, Linear
class BasicBlock(nn.Module):
def __init__(self, in_features, out_features) -> None:
super().__init__()
self.in_features = in_features
self.out_features = out_features
stride = 1
_features = out_features
if self.in_features != self.out_features:
# 在输入通道和输出通道不相等的情况下计算通道是否为2倍差值
if self.out_features / self.in_features == 2.0:
stride = 2 # 在输出特征是输入特征的2倍的情况下 要想参数不翻倍 步长就必须翻倍
else:
raise ValueError("输出特征数最多为输入特征数的2倍!")
self.conv1 = Conv2d(in_features, _features, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = BatchNorm2d(_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
self.relu = ReLU(inplace=True)
self.conv2 = Conv2d(_features, _features, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = BatchNorm2d(_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
# 下采样
self.downsample = None if self.in_features == self.out_features else nn.Sequential(
Conv2d(in_features, out_features, kernel_size=1, stride=2, bias=False),
BatchNorm2d(out_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
# 输入输出的特征数不同时使用下采样层
if self.in_features != self.out_features:
identity = self.downsample(x)
# 残差求和
out += identity
out = self.relu(out)
return out
class ResNet18(nn.Module):
def __init__(self) -> None:
super().__init__()
self.conv1 = Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
self.relu = ReLU(inplace=True)
# self.maxpool = MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
self.layer1 = nn.Sequential(
BasicBlock(64, 64),
BasicBlock(64, 64)
)
self.layer2 = nn.Sequential(
BasicBlock(64, 128),
BasicBlock(128, 128)
)
self.layer3 = nn.Sequential(
BasicBlock(128, 256),
BasicBlock(256, 256)
)
self.layer4 = nn.Sequential(
BasicBlock(256, 512),
BasicBlock(512, 512)
)
self.avgpool = AdaptiveAvgPool2d(output_size=(1, 1))
self.fc = Linear(in_features=512, out_features=10, bias=True)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
# x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x) # <---- 输出为{Tensor:(64,512,1,1)}
x = torch.flatten(x, 1) # <----------------这里是个坑 很容易漏 从池化层到全连接需要一个压平 输出为{Tensor:(64,512)}
x = self.fc(x) # <------------ 输出为{Tensor:(64,10)}
return x
# 模型数据验证
if __name__ == "__main__":
mode = ResNet18()
print(mode)
data = torch.ones((64, 3, 32, 32))
output = mode(data)
print(output.shape)
图片增强
transforms.Compose([
transforms.ToTensor()
, transforms.RandomCrop(32, padding=4) # 先四周填充0,在吧图像随机裁剪成32*32
, transforms.RandomHorizontalFlip(p=0.5) # 随机水平翻转 选择一个概率概率
, transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 均值,标准差
])
最开始我想的是为了避免过拟合,数据增强这里可以多做一些,所以图片旋转,水平翻转,垂直翻转,去色,随机调整色相饱和度对比度亮度都做了,但是验证集准确率死活上不了90%,一轮测试要搞几个小时,实在是吐血啊!
有兴趣的可以试试:
train_transforms = transforms.Compose([
transforms.RandomRotation(45) # 随机旋转,-45到45度之间随机选
, transforms.RandomCrop(32, padding=4) # 先四周填充0,在吧图像随机裁剪成32*32
, transforms.RandomHorizontalFlip(p=0.5) # 随机水平翻转 选择一个概率概率
, transforms.RandomVerticalFlip(p=0.5) # 随机垂直翻转
, transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1)
# 参数1为亮度,参数2为对比度,参数3为饱和度,参数4为色相 全部是随机变化
, transforms.RandomGrayscale(p=0.025) # 概率转换成灰度率,3通道就是R=G=B
, transforms.ToTensor()
, transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 均值,标准差
])
还有一个坑,不要把CIFIA10的数据放大到224,然后直接用Resnet18训练,这样会很慢的,图片的预处理都是在CPU上完成的。暂时没找到什么好办法把预处理放到GPU上做。
如果还想再快点可以把CIFIA10的训练集划分成8:2的比例80%做训练集,20%做验证集,数据也够,一样能出结果,最后在CIFIA10的验证集上跑一把测试结果。
完整代码:
# 使用Resnet18原始model
# 用完整的测试集和验证集
import time
import copy
import numpy as np
import torch
import torchvision.models
from tqdm import tqdm
from torchvision.transforms import transforms
from torch import nn, optim
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10
from torch.utils.tensorboard import SummaryWriter
if __name__ == "__main__":
# 这里面的变量都相当于全局变量 !!
# GPU计算
device = torch.device("cuda")
# 训练总轮数
total_epochs = 250
# 每次取出样本数
batch_size = 128
# 初始学习率
Lr = 0.1
DATASET_PATH = '/kaggle/input/cifar10-python'
SAVE_PATH = '/kaggle/working/'
filename = '{}best_cnn_model'.format(SAVE_PATH) # 文件扩展名在保存时添加
torch.backends.cudnn.benchmark = True
# 准备数据
data_transforms = {
'train': transforms.Compose([
transforms.ToTensor()
, transforms.RandomCrop(32, padding=4) # 先四周填充0,在吧图像随机裁剪成32*32
, transforms.RandomHorizontalFlip(p=0.5) # 随机水平翻转 选择一个概率概率
, transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 均值,标准差
]),
'valid': transforms.Compose([
transforms.ToTensor()
, transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
}
# 准备数据 这里将训练集和验证集写到了一个list里 否则后面的训练与验证阶段重复代码太多
image_datasets = {
x: CIFAR10(DATASET_PATH, train=True if x == 'train' else False,
transform=data_transforms[x], download=True) for x in ['train', 'valid']}
dataloaders: dict = {
x: torch.utils.data.DataLoader(
image_datasets[x], batch_size=batch_size, shuffle=True if x == 'train' else False
) for x in ['train', 'valid']
}
# 定义模型
model_ft = torchvision.models.resnet18(pretrained=False)
# 修改模型
model_ft.conv1 = nn.Conv2d(3, 64, 3, stride=1, padding=1, bias=False) # 首层改成3x3卷积核
model_ft.maxpool = nn.MaxPool2d(1, 1, 0) # 图像太小 本来就没什么特征 所以这里通过1x1的池化核让池化层失效
num_ftrs = model_ft.fc.in_features # 获取(fc)层的输入的特征数
model_ft.fc = nn.Linear(num_ftrs, 10)
model_ft.to(device)
# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn.to(device)
# 训练模型
# 显示要训练的模型
print("==============当前模型要训练的层==============")
for name, params in model_ft.named_parameters():
if params.requires_grad:
print(name)
# 训练模型所需参数
# 用于记录损失值未发生变化batch数
counter = 0
# 记录训练次数
total_step = {
'train': 0, 'valid': 0
}
# 记录开始时间
since = time.time()
# 记录当前最小损失值
valid_loss_min = np.Inf
# 保存模型文件的尾标
save_num = 0
# 保存最优正确率
best_acc = 0
for epoch in range(total_epochs):
# 动态调整学习率
if counter / 10 == 1:
counter = 0
Lr = Lr * 0.5
# 在每个epoch里重新创建优化器???
optimizer = optim.SGD(model_ft.parameters(), lr=Lr, momentum=0.9, weight_decay=5e-4)
print('Epoch {}/{}'.format(epoch + 1, total_epochs))
print('-' * 10)
print()
# 训练和验证 每一轮都是先训练train 再验证valid
for phase in ['train', 'valid']:
# 调整模型状态
if phase == 'train':
model_ft.train() # 训练
else:
model_ft.eval() # 验证
# 记录损失值
running_loss = 0.0
# 记录正确个数
running_corrects = 0
# 一次读取一个batch里面的全部数据
for inputs, labels in tqdm(dataloaders[phase]):
inputs = inputs.to(device)
labels = labels.to(device)
# 梯度清零
optimizer.zero_grad()
# 只有训练的时候计算和更新梯度
with torch.set_grad_enabled(phase == 'train'):
outputs = model_ft(inputs)
loss = loss_fn(outputs, labels)
# torch.max() 返回的是一个元组 第一个参数是返回的最大值的数值 第二个参数是最大值的序号
_, preds = torch.max(outputs, 1) # 前向传播 这里可以测试 在valid时梯度是否变化
# 训练阶段更新权重
if phase == 'train':
loss.backward() # 反向传播
optimizer.step() # 优化权重
# TODO:在SummaryWriter中记录学习率
# ....
# 计算损失值
running_loss += loss.item() * inputs.size(0) # loss计算的是平均值,所以要乘上batch-size,计算损失的总和
running_corrects += (preds == labels).sum() # 计算预测正确总个数
# 每个batch加1次
total_step[phase] += 1
# 一轮训练完后计算损失率和正确率
epoch_loss = running_loss / len(dataloaders[phase].sampler) # 当前轮的总体平均损失值
epoch_acc = float(running_corrects) / len(dataloaders[phase].sampler) # 当前轮的总正确率
time_elapsed = time.time() - since
print()
print('当前总耗时 {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('{} Loss: {:.4f}[{}] Acc: {:.4f}'.format(phase, epoch_loss, counter, epoch_acc))
if phase == 'valid':
# 得到最好那次的模型
if epoch_loss < valid_loss_min: # epoch_acc > best_acc:
best_acc = epoch_acc
# 保存当前模型
best_model_wts = copy.deepcopy(model_ft.state_dict())
state = {
'state_dict': model_ft.state_dict(),
'best_acc': best_acc,
'optimizer': optimizer.state_dict(),
}
# 只保存最近2次的训练结果
save_num = 0 if save_num > 1 else save_num
save_name_t = '{}_{}.pth'.format(filename, save_num)
torch.save(state, save_name_t) # \033[1;31m 字体颜色:红色\033[0m
print("已保存最优模型,准确率:\033[1;31m {:.2f}%\033[0m,文件名:{}".format(best_acc * 100, save_name_t))
save_num += 1
valid_loss_min = epoch_loss
counter = 0
else:
counter += 1
print()
print('当前学习率 : {:.7f}'.format(optimizer.param_groups[0]['lr']))
print()
# 训练结束
time_elapsed = time.time() - since
print()
print('任务完成!')
print('任务完成总耗时 {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('最高验证集准确率: {:4f}'.format(best_acc))
save_num = save_num - 1
save_num = save_num if save_num < 0 else 1
save_name_t = '{}_{}.pth'.format(filename, save_num)
print('最优模型保存在:{}'.format(save_name_t))
运行结果片段:
Epoch 1/250 ---------- 100%|██████████| 391/391 [00:50<00:00, 7.80it/s] 当前总耗时 0m 50s train Loss: 1.8680[0] Acc: 0.3292 100%|██████████| 79/79 [00:03<00:00, 20.67it/s] 当前总耗时 0m 54s valid Loss: 1.4895[0] Acc: 0.4499 已保存最优模型,准确率: 44.99%,文件名:/kaggle/working/best_cnn_model_0.pth 当前学习率 : 0.1000000 Epoch 11/250 ---------- 100%|██████████| 391/391 [00:41<00:00, 9.33it/s] 当前总耗时 8m 31s train Loss: 0.4722[2] Acc: 0.8391 100%|██████████| 79/79 [00:03<00:00, 21.90it/s] 当前总耗时 8m 35s valid Loss: 0.5824[2] Acc: 0.8068 已保存最优模型,准确率: 80.68%,文件名:/kaggle/working/best_cnn_model_0.pth 当前学习率 : 0.1000000 Epoch 46/250 ---------- 100%|██████████| 391/391 [00:41<00:00, 9.46it/s] 当前总耗时 35m 11s train Loss: 0.2126[0] Acc: 0.9276 100%|██████████| 79/79 [00:03<00:00, 21.00it/s] 当前总耗时 35m 15s valid Loss: 0.2970[0] Acc: 0.9004 已保存最优模型,准确率: 90.04%,文件名:/kaggle/working/best_cnn_model_1.pth 当前学习率 : 0.0500000 Epoch 101/250 ---------- 100%|██████████| 391/391 [00:42<00:00, 9.23it/s] 当前总耗时 77m 28s train Loss: 0.0037[0] Acc: 0.9995 100%|██████████| 79/79 [00:03<00:00, 21.22it/s] 当前总耗时 77m 32s valid Loss: 0.1916[0] Acc: 0.9500 已保存最优模型,准确率: 95.00%,文件名:/kaggle/working/best_cnn_model_1.pth 当前学习率 : 0.0031250 Epoch 128/250 ---------- 100%|██████████| 391/391 [00:42<00:00, 9.27it/s] 当前总耗时 98m 10s train Loss: 0.0021[4] Acc: 0.9999 100%|██████████| 79/79 [00:03<00:00, 21.68it/s] 当前总耗时 98m 14s valid Loss: 0.1775[4] Acc: 0.9522 已保存最优模型,准确率: 95.22%,文件名:/kaggle/working/best_cnn_model_0.pth 当前学习率 : 0.0031250 Epoch 129/250 ---------- 100%|██████████| 391/391 [00:41<00:00, 9.32it/s] 当前总耗时 98m 56s train Loss: 0.0020[0] Acc: 1.0000 100%|██████████| 79/79 [00:03<00:00, 21.31it/s] 当前总耗时 98m 59s valid Loss: 0.1786[0] Acc: 0.9529 当前学习率 : 0.0031250
最后吐槽下google的Colab的那个 Colab Pro 这玩意并没有什么卵用,100个运行时很快就烧完,然后你就和白嫖用户一样了,不要花这冤枉钱,直接白嫖,存最近的两个最优模型,这样就是被踢了至少还能明天继续。或者把模型下载下来在传到Kaggle上继续白嫖。
不过最好还是能有自己的GPU,代码跑起来不用守着,可以去睡觉了,睡醒了再看结果。但是现在的显卡价格实在是一言难尽啊……
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
我看到这个错误:translationmissing:da.datetime.distance_in_words.about_x_hours我的语言环境文件:http://pastie.org/2944890我的看法:我已将其添加到我的application.rb中:config.i18n.load_path+=Dir[Rails.root.join('my','locales','*.{rb,yml}').to_s]config.i18n.default_locale=:da如果我删除I18配置,帮助程序会处理英语。更新:我在config/enviorments/devolpment
如果我使用ruby版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更
大家好!我对我的:username字段进行了一个小的验证,它应该是4到30个字符。我写了一个验证::length=>{:within=>4..30,:message=>I18n.t('activerecord.errors.range')-我想显示一个错误各种错误的消息(不像,太长或太短),但这里有一个问题-我可以将最小值和最大值都传递给翻译,以便有类似的东西:用户名应该在4到30个字符之间。目前我有:range:"shouldbebetween%{count}and%{count}characters",这显然不起作用(只是为了检查)。是否可以从范围中获取这些值?谢谢大家的指教!
我刚刚安装了带有RVM的Ruby2.2.0,并尝试使用它得到了这个:$rvmuse2.2.0--defaultUsing/Users/brandon/.rvm/gems/ruby-2.2.0dyld:Librarynotloaded:/usr/local/lib/libgmp.10.dylibReferencedfrom:/Users/brandon/.rvm/rubies/ruby-2.2.0/bin/rubyReason:Incompatiblelibraryversion:rubyrequiresversion13.0.0orlater,butlibgmp.10.dylibpro
我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da
我已经通过提供MagickWand.h的路径尝试了一切,我安装了命令工具。谁能帮帮我?$geminstallrmagick-v2.13.1Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingrmagick:ERROR:Failedtobuildgemnativeextension./Users/ghazanfarali/.rvm/rubies/ruby-1.8.7-p357/bin/rubyextconf.rbcheckingforRubyversion>=1.8.5...yescheckingfor/
如果特定语言环境中缺少翻译,如何配置i18n以使用en语言环境翻译?当前已插入翻译缺失消息。我正在使用RoR3.1。 最佳答案 找到相似的question这里是答案:#application.rb#railswillfallbacktoconfig.i18n.default_localetranslationconfig.i18n.fallbacks=true#railswillfallbacktoen,nomatterwhatissetasconfig.i18n.default_localeconfig.i18n.fallback
我正在使用macos,我想使用ruby驱动程序连接到sqlserver。我想使用tiny_tds,但它给出了缺少free_tds的错误,但它已经安装了。怎么能过这个?~brewinstallfreetdsWarning:freetds-0.91.112alreadyinstalled~sudogeminstalltiny_tdsBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtiny_tds:ERROR:Failedtobuildgemnativeextension.完整日志如下:/System
我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat