Colab/PyTorch - 003 Transfer Learning For Image Classification
Colab/PyTorch - 003 Transfer Learning For Image Classification
- 1. 源由
- 2. 迁移学习(ResNet50)
- 2.1 数据集准备
- 2.2 数据增强
- 2.3 数据加载
- 2.4 迁移学习
- 2.5 数据集训练&验证
- 2.6 模型推理
- 3. 总结
- 4. 参考资料
1. 源由
迁移学习已经彻底改变了 PyTorch 中处理图像分类的方式。
最近,PyTorch 因其易用性和学习性而受到了广泛关注。特斯拉人工智能高级总监安德烈·卡帕西在他的推特中说了以下的话。
PyTorch 非常透明,可以帮助研究人员和数据科学家实现高生产力和可靠的结果。
其实,这种方法,有点类似螺旋式上升的旋转阶梯。大量的学习让我们积累知识螺旋式上升到某个层面。当这个层面遇到新的类似问题时,只要经过适当的调整,就能更上一层楼。
而这个适当的调整并不需要大量的推倒重来,更多的是在原有的基础上进行适配调整。
在《Colab/PyTorch - 002 Pre Trained Models for Image Classification》中,放入一个没有训练过的图片,可以看到机器学习给出了一个相近的分类,俗语说“难字读半边”,大体就是这个意思。
在《Jammy@Jetson Orin - Tensorflow & Keras Get Started: Transfer Learning & Fine Tuning》,也讨论过关于Transfer Learning的概念。
2. 迁移学习(ResNet50)
基于ImageNet用数百万张图像进行了训练的ResNet50模型,对CalTech256数据集的子集来对10种动物的图像进行分类就是本次PyTorch的一个例子。
通过据集准备、数据增强、构建分类器、使用迁移学习来利用低级图像特征,比如边缘、纹理等模型参数特征(这些特征是预训练模型ResNet50继承得来的)。最后,通过训练集来进一步训练分类器,从而学习数据集图像中的更高级别的细节,比如眼睛、腿等。
2.1 数据集准备
CalTech256数据集包含30,607张图像,分为256个不同的标签类别,还有一个“杂乱”类别。对整个数据集进行训练需要数小时。
因此,我们将使用数据集的一个子集,其中包含10种动物:熊、黑猩猩、长颈鹿、大猩猩、美洲驼、鸵鸟、豪猪、臭鼬、三角龙和斑马。这些文件夹中的图像数量从81张(对于臭鼬)到212张(对于大猩猩)不等。
我们对这些图片进行如下分类:
- 每个类别中的前60张图像进行训练
- 接下来的10张图像用于验证
- 其余的图像用于下面实验中的测试
因此,最终,我们有600张训练图像、100张验证图像、409张测试图像和10个动物类别。
操作步骤如下:
- 下载 CalTech256数据集
- 创建名为 train、valid 和 test 的三个目录。
- 在 train/valid/test 目录中分别创建 10 个子目录。这些子目录应该命名为 bear、chimp、giraffe、gorilla、llama、ostrich、porcupine、skunk、triceratops 和 zebra。
- 将 Caltech256 数据集中的前60张熊的图像移动到目录 train/bear。对每种动物重复此步骤。
- 将 Caltech256 数据集中的下一组10张熊的图像移动到目录 valid/bear。对每种动物重复此步骤。
- 将熊的剩余图像(即未包含在 train 或 valid 文件夹中的图像)复制到目录 test/bear。对每种动物重复此步骤。
2.2 数据增强
在每个 epoch 中,每个输入图像会经过多次变换,通过在变换中引入一些随机性来插入一些变化。
当训练多个 epoch 时,模型会看到输入图像都是新的随机变换。这导致了数据增强,然后模型试图更加泛化。
下面看到了一张三角龙图像的变换版本的示例:
# Applying Transforms to the Data
image_transforms = { 'train': transforms.Compose([transforms.RandomResizedCrop(size=256, scale=(0.8, 1.0)),transforms.RandomRotation(degrees=15),transforms.RandomHorizontalFlip(),transforms.CenterCrop(size=224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])]),'valid': transforms.Compose([transforms.Resize(size=256),transforms.CenterCrop(size=224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])]),'test': transforms.Compose([transforms.Resize(size=256),transforms.CenterCrop(size=224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])])
}
transform.RandomResizedCrop
通过随机大小裁剪输入图像(在原始大小的0.8到1.0的范围内,并在默认范围内的0.75到1.33的随机宽高比)。然后将裁剪后的图像调整大小为256×256。transform.RandomRotation
将图像随机旋转一个角度,角度范围为-15度到15度。transform.RandomHorizontalFlip
以默认概率50%随机水平翻转图像。transform.CenterCrop
从中心裁剪出一个224×224的图像。transform.ToTensor
将 PIL 图像转换为浮点数张量,并将值归一化到0-1的范围,方法是将其除以255。transform.Normalize
接受一个3通道张量,并按照该通道的输入均值和标准差对每个通道进行归一化。均值和标准差向量作为3元素向量输入。张量中的每个通道被规范化为 T = (T - 均值) / 标准差。
所有上述转换都使用train的transforms.Compose
连接在一起。
对于验证和测试数据,我们不执行RandomResizedCrop、RandomRotation和RandomHorizontalFlip转换。相反,我们将验证图像调整大小为256×256,并裁剪出中心224×224的部分,以便能够将它们与预训练模型一起使用。最后,图像被转换为张量,并按照ImageNet中所有图像的均值和标准差进行归一化处理。
2.3 数据加载
Colab上运行,需要将制作好的数据集上传Google云存储。
数据存放目录如下所示:
├── caltech_10
│ ├── test
│ │ ├── bear
│ │ ├── chimp
│ │ ├── giraffe
│ │ ├── gorilla
│ │ ├── llama
│ │ ├── ostrich
│ │ ├── porcupine
│ │ ├── skunk
│ │ ├── triceratops
│ │ └── zebra
│ ├── train
│ │ ├── bear
│ │ ├── chimp
│ │ ├── giraffe
│ │ ├── gorilla
│ │ ├── llama
│ │ ├── ostrich
│ │ ├── porcupine
│ │ ├── skunk
│ │ ├── triceratops
│ │ └── zebra
│ └── valid
│ ├── bear
│ ├── chimp
│ ├── giraffe
│ ├── gorilla
│ ├── llama
│ ├── ostrich
│ ├── porcupine
│ ├── skunk
│ ├── triceratops
│ └── zebra
└── image_classification_using_transfer_learning_in_pytorch.ipynb
使用DataLoader
加载用于训练的数据:
# Test on Google Drivefrom google.colab import drive
drive.mount('/content/drive')# Load the Data# Set train and valid directory pathsdataset = '/content/drive/MyDrive/caltech_10'train_directory = os.path.join(dataset, 'train')
valid_directory = os.path.join(dataset, 'valid')
test_directory = os.path.join(dataset, 'test')# Batch size
bs = 32# Number of classes
num_classes = len(os.listdir(valid_directory)) #10#2#257
print(num_classes)# Load Data from folders
data = {'train': datasets.ImageFolder(root=train_directory, transform=image_transforms['train']),'valid': datasets.ImageFolder(root=valid_directory, transform=image_transforms['valid']),'test': datasets.ImageFolder(root=test_directory, transform=image_transforms['test'])
}# Get a mapping of the indices to the class names, in order to see the output classes of the test images.
idx_to_class = {v: k for k, v in data['train'].class_to_idx.items()}
print(idx_to_class)# Size of Data, to be used for calculating Average Loss and Accuracy
train_data_size = len(data['train'])
valid_data_size = len(data['valid'])
test_data_size = len(data['test'])# Create iterators for the Data loaded using DataLoader module
train_data_loader = DataLoader(data['train'], batch_size=bs, shuffle=True)
valid_data_loader = DataLoader(data['valid'], batch_size=bs, shuffle=True)
test_data_loader = DataLoader(data['test'], batch_size=bs, shuffle=True)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(train_data_size, valid_data_size, test_data_size)
2.4 迁移学习
从头开始训练分类器是非常困难且耗时的。
因此,使用一个预训练的模型作为基础,并改变最后几层以根据我们想要的类别对图像进行分类。这有助于我们在一个小数据集上获得良好的结果,因为预训练模型已经从像ImageNet这样的更大数据集中学习了基本的图像特征。
正如上面图像中所看到的,内部层保持与预训练ResNet50模型相同,只有最后几层被更改以适应我们的类别数量。
# Load pretrained ResNet50 Model
resnet50 = models.resnet50(pretrained=True)
resnet50 = resnet50.to(device)
Canziani等人列出了许多预训练模型,用于各种实际应用,分析了每个模型的准确性和推断所需的时间。
ResNet50是一个在准确性和推断时间之间有良好折衷的模型之一。当在PyTorch中加载模型时,默认情况下,所有参数的’requires_grad’字段都设置为true。这意味着对参数值的每一次更改都将被存储以用于训练时使用的反向传播图中。这增加了内存需求。由于我们预训练模型中的大多数参数已经训练过,我们将’requires_grad’字段重置为false。
# Freeze model parameters
for param in resnet50.parameters():param.requires_grad = False
接下来,我们将ResNet50模型的最后一层替换为一小组Sequential层。ResNet50的最后一个全连接层的输入被馈送到一个线性层。它有256个输出,然后被馈送到ReLU和Dropout层。然后是一个256×10的线性层,它有10个输出,对应于我们CalTech子集中的10个类别。
# Change the final layer of ResNet50 Model for Transfer Learning
fc_inputs = resnet50.fc.in_featuresresnet50.fc = nn.Sequential(nn.Linear(fc_inputs, 256),nn.ReLU(),nn.Dropout(0.4),nn.Linear(256, num_classes), # Since 10 possible outputsnn.LogSoftmax(dim=1) # For using NLLLoss()
)
由于我们将在GPU上进行训练,我们准备好将模型移到GPU上。
# Convert model to be used on GPU
resnet50 = resnet50.to(device)
接下来,我们定义用于训练的损失函数和优化器。PyTorch提供了多种损失函数。我们使用负对数似然损失函数,因为它对于多类别分类很有用。PyTorch还支持多种优化器。我们使用Adam优化器。Adam是最流行的优化器之一,因为它可以针对每个参数单独调整学习率。
# Define Optimizer and Loss Function
loss_func = nn.NLLLoss()
optimizer = optim.Adam(resnet50.parameters())
2.5 数据集训练&验证
训练是在固定的epoch数量内进行的,每个图像在单个epoch中处理一次。训练数据加载器以批量方式加载数据。我们给定了一个批量大小为32。这意味着每个批次最多可以有32张图像。
对于每个批次,输入图像被传递到模型中,即前向传播,以获取输出。然后使用提供的损失函数或成本函数使用真实值和计算得到的输出来计算损失。使用反向函数计算损失相对于可训练参数的梯度。
注:使用迁移学习时,只需要计算属于模型末尾的少数新添加层的一小部分参数的梯度。对模型的摘要函数调用可以显示实际参数数量和可训练参数的数量。这种方法中的优势是只需要训练总模型参数的大约十分之一。
梯度计算是使用自动求导和反向传播完成的,在图中使用链式法则进行微分。PyTorch在反向传播过程中累积所有梯度。因此,在训练循环的开始时将它们清零是至关重要的。这可以通过优化器的zero_grad函数实现。最后,在反向传播过程中计算了梯度后,使用优化器的step函数更新参数。
对整个批次计算总损失和准确度,然后对所有批次进行平均,以获得整个epoch的损失和准确度值。
随着训练进行的增加,模型倾向于过度拟合数据,导致其在新的测试数据上表现不佳。保持一个单独的验证集很重要,这样我们就可以在合适的时机停止训练,并防止过拟合。在每个epoch的训练循环之后立即进行验证。由于在验证过程中我们不需要进行任何梯度计算,所以它是在一个torch.no_grad()块内完成的。
对于每个验证批次,输入和标签被传输到GPU(如果cuda可用,否则它们被传输到CPU)。输入经过前向传播,然后对批次和循环结束时的整个epoch进行损失和准确度计算。
def train_and_validate(model, loss_criterion, optimizer, epochs=25):'''Function to train and validateParameters:param model: Model to train and validate:param loss_criterion: Loss Criterion to minimize:param optimizer: Optimizer for computing gradients:param epochs: Number of epochs (default=25)Returnsmodel: Trained Model with best validation accuracyhistory: (dict object): Having training loss, accuracy and validation loss, accuracy'''start = time.time()history = []best_loss = 100000.0best_epoch = Nonefor epoch in range(epochs):epoch_start = time.time()print("Epoch: {}/{}".format(epoch+1, epochs))# Set to training modemodel.train()# Loss and Accuracy within the epochtrain_loss = 0.0train_acc = 0.0valid_loss = 0.0valid_acc = 0.0for i, (inputs, labels) in enumerate(train_data_loader):inputs = inputs.to(device)labels = labels.to(device)# Clean existing gradientsoptimizer.zero_grad()# Forward pass - compute outputs on input data using the modeloutputs = model(inputs)# Compute lossloss = loss_criterion(outputs, labels)# Backpropagate the gradientsloss.backward()# Update the parametersoptimizer.step()# Compute the total loss for the batch and add it to train_losstrain_loss += loss.item() * inputs.size(0)# Compute the accuracyret, predictions = torch.max(outputs.data, 1)correct_counts = predictions.eq(labels.data.view_as(predictions))# Convert correct_counts to float and then compute the meanacc = torch.mean(correct_counts.type(torch.FloatTensor))# Compute total accuracy in the whole batch and add to train_acctrain_acc += acc.item() * inputs.size(0)#print("Batch number: {:03d}, Training: Loss: {:.4f}, Accuracy: {:.4f}".format(i, loss.item(), acc.item()))# Validation - No gradient tracking neededwith torch.no_grad():# Set to evaluation modemodel.eval()# Validation loopfor j, (inputs, labels) in enumerate(valid_data_loader):inputs = inputs.to(device)labels = labels.to(device)# Forward pass - compute outputs on input data using the modeloutputs = model(inputs)# Compute lossloss = loss_criterion(outputs, labels)# Compute the total loss for the batch and add it to valid_lossvalid_loss += loss.item() * inputs.size(0)# Calculate validation accuracyret, predictions = torch.max(outputs.data, 1)correct_counts = predictions.eq(labels.data.view_as(predictions))# Convert correct_counts to float and then compute the meanacc = torch.mean(correct_counts.type(torch.FloatTensor))# Compute total accuracy in the whole batch and add to valid_accvalid_acc += acc.item() * inputs.size(0)#print("Validation Batch number: {:03d}, Validation: Loss: {:.4f}, Accuracy: {:.4f}".format(j, loss.item(), acc.item()))if valid_loss < best_loss:best_loss = valid_lossbest_epoch = epoch# Find average training loss and training accuracyavg_train_loss = train_loss/train_data_size avg_train_acc = train_acc/train_data_size# Find average training loss and training accuracyavg_valid_loss = valid_loss/valid_data_size avg_valid_acc = valid_acc/valid_data_sizehistory.append([avg_train_loss, avg_valid_loss, avg_train_acc, avg_valid_acc])epoch_end = time.time()print("Epoch : {:03d}, Training: Loss - {:.4f}, Accuracy - {:.4f}%, \n\t\tValidation : Loss - {:.4f}, Accuracy - {:.4f}%, Time: {:.4f}s".format(epoch, avg_train_loss, avg_train_acc*100, avg_valid_loss, avg_valid_acc*100, epoch_end-epoch_start))# Save if the model has best accuracy till nowtorch.save(model, dataset+'_model_'+str(epoch)+'.pt')return model, history, best_epoch
Epoch: 1/30
Epoch : 000, Training: Loss - 1.6488, Accuracy - 46.3333%, Validation : Loss - 0.6532, Accuracy - 94.0000%, Time: 69.4381s
Epoch: 2/30
Epoch : 001, Training: Loss - 0.5886, Accuracy - 87.1667%, Validation : Loss - 0.3524, Accuracy - 89.0000%, Time: 8.7085s
Epoch: 3/30
Epoch : 002, Training: Loss - 0.3438, Accuracy - 92.1667%, Validation : Loss - 0.2165, Accuracy - 97.0000%, Time: 9.7899s
Epoch: 4/30
Epoch : 003, Training: Loss - 0.2306, Accuracy - 94.8333%, Validation : Loss - 0.2157, Accuracy - 93.0000%, Time: 8.2054s
Epoch: 5/30
Epoch : 004, Training: Loss - 0.1882, Accuracy - 95.5000%, Validation : Loss - 0.1668, Accuracy - 96.0000%, Time: 9.5612s
Epoch: 6/30
Epoch : 005, Training: Loss - 0.1682, Accuracy - 95.3333%, Validation : Loss - 0.1474, Accuracy - 97.0000%, Time: 8.0876s
Epoch: 7/30
Epoch : 006, Training: Loss - 0.1850, Accuracy - 94.8333%, Validation : Loss - 0.1665, Accuracy - 96.0000%, Time: 8.8183s
Epoch: 8/30
Epoch : 007, Training: Loss - 0.1645, Accuracy - 96.5000%, Validation : Loss - 0.1560, Accuracy - 94.0000%, Time: 8.5604s
Epoch: 9/30
Epoch : 008, Training: Loss - 0.1490, Accuracy - 94.3333%, Validation : Loss - 0.1529, Accuracy - 95.0000%, Time: 8.7029s
Epoch: 10/30
Epoch : 009, Training: Loss - 0.1236, Accuracy - 95.8333%, Validation : Loss - 0.1578, Accuracy - 96.0000%, Time: 8.4867s
Epoch: 11/30
Epoch : 010, Training: Loss - 0.0958, Accuracy - 97.8333%, Validation : Loss - 0.2119, Accuracy - 92.0000%, Time: 9.2300s
Epoch: 12/30
Epoch : 011, Training: Loss - 0.1213, Accuracy - 96.5000%, Validation : Loss - 0.1623, Accuracy - 95.0000%, Time: 8.8091s
Epoch: 13/30
Epoch : 012, Training: Loss - 0.1710, Accuracy - 94.0000%, Validation : Loss - 0.2524, Accuracy - 89.0000%, Time: 9.2230s
Epoch: 14/30
Epoch : 013, Training: Loss - 0.1252, Accuracy - 95.6667%, Validation : Loss - 0.1514, Accuracy - 96.0000%, Time: 8.6822s
Epoch: 15/30
Epoch : 014, Training: Loss - 0.0881, Accuracy - 97.6667%, Validation : Loss - 0.1434, Accuracy - 95.0000%, Time: 8.8090s
Epoch: 16/30
Epoch : 015, Training: Loss - 0.0828, Accuracy - 96.8333%, Validation : Loss - 0.1271, Accuracy - 96.0000%, Time: 8.4651s
Epoch: 17/30
Epoch : 016, Training: Loss - 0.0705, Accuracy - 97.8333%, Validation : Loss - 0.1536, Accuracy - 96.0000%, Time: 9.4177s
Epoch: 18/30
Epoch : 017, Training: Loss - 0.0834, Accuracy - 97.6667%, Validation : Loss - 0.1726, Accuracy - 94.0000%, Time: 8.0583s
Epoch: 19/30
Epoch : 018, Training: Loss - 0.0740, Accuracy - 98.1667%, Validation : Loss - 0.1585, Accuracy - 95.0000%, Time: 9.0694s
Epoch: 20/30
Epoch : 019, Training: Loss - 0.0659, Accuracy - 98.3333%, Validation : Loss - 0.2178, Accuracy - 93.0000%, Time: 8.6098s
Epoch: 21/30
Epoch : 020, Training: Loss - 0.0819, Accuracy - 97.8333%, Validation : Loss - 0.1866, Accuracy - 95.0000%, Time: 8.9363s
Epoch: 22/30
Epoch : 021, Training: Loss - 0.0921, Accuracy - 96.5000%, Validation : Loss - 0.1907, Accuracy - 95.0000%, Time: 8.2608s
Epoch: 23/30
Epoch : 022, Training: Loss - 0.0662, Accuracy - 98.0000%, Validation : Loss - 0.1494, Accuracy - 95.0000%, Time: 11.4841s
Epoch: 24/30
Epoch : 023, Training: Loss - 0.0618, Accuracy - 98.0000%, Validation : Loss - 0.1616, Accuracy - 94.0000%, Time: 9.5806s
Epoch: 25/30
Epoch : 024, Training: Loss - 0.0767, Accuracy - 97.0000%, Validation : Loss - 0.1904, Accuracy - 94.0000%, Time: 8.1954s
Epoch: 26/30
Epoch : 025, Training: Loss - 0.0476, Accuracy - 98.8333%, Validation : Loss - 0.1830, Accuracy - 94.0000%, Time: 9.1184s
Epoch: 27/30
Epoch : 026, Training: Loss - 0.0575, Accuracy - 98.3333%, Validation : Loss - 0.2433, Accuracy - 94.0000%, Time: 8.7180s
Epoch: 28/30
Epoch : 027, Training: Loss - 0.0831, Accuracy - 97.1667%, Validation : Loss - 0.2413, Accuracy - 93.0000%, Time: 8.5477s
Epoch: 29/30
Epoch : 028, Training: Loss - 0.0649, Accuracy - 98.0000%, Validation : Loss - 0.2413, Accuracy - 93.0000%, Time: 9.0394s
Epoch: 30/30
Epoch : 029, Training: Loss - 0.0647, Accuracy - 97.5000%, Validation : Loss - 0.1586, Accuracy - 94.0000%, Time: 9.5677s
如上图所示,对于这个数据集,验证和训练损失都相当快地稳定下来。准确度也迅速提高到了0.9左右的范围。随着epoch数量的增加,训练损失进一步减小,导致过拟合,但验证结果并没有显著改善。因此,我们选择了具有更高准确度和较低损失的epoch的模型。最好是在早期停止以防止过拟合训练数据。在我们的情况下,我们选择了第8个epoch,其验证准确度为96%。
2.6 模型推理
一旦我们有了模型,我们就可以对单个测试图像或整个测试数据集进行推断,以获取测试准确度。测试集准确度的计算与验证代码类似,只是它是在测试数据集上进行的。我们已经在Python笔记本中包含了computeTestSetAccuracy函数来完成相同的工作。让我们在下面讨论如何为给定的测试图像找到输出类别。
首先,输入图像经历用于验证/测试数据的所有转换。然后将结果张量转换为四维张量,并通过模型传递,该模型输出不同类别的对数概率。对模型输出的指数给出了类别概率。然后我们选择具有最高概率的类作为我们的输出类别。选择具有最高概率的类作为我们的输出类别。
def predict(model, test_image_name):'''Function to predict the class of a single test imageParameters:param model: Model to test:param test_image_name: Test image'''transform = image_transforms['test']test_image = Image.open(test_image_name)plt.imshow(test_image)test_image_tensor = transform(test_image)if torch.cuda.is_available():test_image_tensor = test_image_tensor.view(1, 3, 224, 224).cuda()else:test_image_tensor = test_image_tensor.view(1, 3, 224, 224)with torch.no_grad():model.eval()# Model outputs log probabilitiesout = model(test_image_tensor)ps = torch.exp(out)topk, topclass = ps.topk(3, dim=1)cls = idx_to_class[topclass.cpu().numpy()[0][0]]score = topk.cpu().numpy()[0][0]for i in range(3):print("Predcition", i+1, ":", idx_to_class[topclass.cpu().numpy()[0][i]], ", Score: ", topk.cpu().numpy()[0][i])
# Test a particular model on a test image
! wget https://cdn.pixabay.com/photo/2018/10/01/12/28/skunk-3716043_1280.jpg -O skunk.jpg
dataset = '/content/drive/MyDrive/caltech_10'
model = torch.load("{}_model_{}.pt".format(dataset, best_epoch))
predict(model, 'skunk.jpg')# Load Data from folders
#computeTestSetAccuracy(model, loss_func)
判断的准确率99.97%。
3. 总结
使用一个在ImageNet的1000个类别上预训练的模型,非常有效地应用了ResNet50模型,通过小数据集的训练,对感兴趣的10个不同类别的图像进行了分类。
测试代码:003 Image Classification using Transfer Learning in Pytorch
4. 参考资料
【1】Colab/PyTorch - Getting Started with PyTorch
相关文章:

Colab/PyTorch - 003 Transfer Learning For Image Classification
Colab/PyTorch - 003 Transfer Learning For Image Classification 1. 源由2. 迁移学习(ResNet50)2.1 数据集准备2.2 数据增强2.3 数据加载2.4 迁移学习2.5 数据集训练&验证2.6 模型推理 3. 总结4. 参考资料 1. 源由 迁移学习已经彻底改变了 PyTorch 中处理图像分类的方式…...

数据结构深入理解--栈
目录 一、栈的定义 二、栈的实现 2.1 栈的结构 2.2 栈的初始化 2.3 栈的销毁 2.3 栈元素的插入 2.4 栈元素的删除 2.5 栈顶元素获取 2.6 栈元素有效个数获取 2.7 栈是否为空判断 三、代码总览 Stack.h Stack.c 测试代码:test.c 四、例题 例一: 例二ÿ…...

Maven 的仓库、周期和插件
优质博文:IT-BLOG-CN 一、Maven 仓库 在Maven的世界中,任何一个依赖、插件或者项目构建的输出,都可以称为构建。Maven在某个统一的位置存储所有项目的共享的构建,这个统一的位置,我们就称之为仓库。任何的构建都有唯一…...

parallels desktop19最新免费Mac电脑虚拟机软件
Parallels Desktop是一款运行在Mac电脑上的虚拟机软件,它允许用户在Mac系统上同时运行多个操作系统,比如Windows、Linux等。通过这款软件,Mac用户可以轻松地在同一台电脑上体验不同操作系统的功能和应用程序,而无需额外的硬件设备…...

波动性悖论:为何低风险股票长期跑赢高风险对手?
从去年开始,“红利低波”类的产品净值稳步向上,不断新高,让很多人关注到了A股“分红高”、“波动率低”这两类股票。分红高的公司更受投资者青睐,这从基本面的角度很容易理解,那么波动率低的股票明明波动更小ÿ…...

环信设置头像昵称(安卓android)版
在此真的要吐槽吐槽环信,那么大的公司,文档那么乱。。。真的像一坨屎一样,翻个demo东翻西翻,官网论坛看的眼瞎。。。几乎要放弃了,还好百度到别人的看了看弄出来了 1、首先,要确认自己用的是哪个环信的UI库…...

Rust:用 Warp 库实现 Restful API 的简单示例
直接上代码: 1、源文件 Cargo.toml [package] name "xcalc" version "0.1.0" edition "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies] warp "…...

【SpringBoot】 什么是springboot(一)?如何搭建springboot项目?
文章目录 SpringBoot第一章1、什么是springboot1、回顾ssm项目搭建流程2、springboot项目的优点2、搭建springboot项目方式1:方式2:第二章1、基本配置1、热部署2、注解3、端口配置application.properties特点application.yml特点注意4、环境配置springboot中的配置文件要求5、…...

从loss角度理解LLM涌现能力
如今的很多研究都表明小模型也能出现涌现能力,本文的作者团队通过大量实验发现模型的涌现能力与模型大小、训练计算量无关,只与预训练loss相关。 作者团队惊奇地发现,不管任何下游任务,不管模型大小,模型出现涌现能力…...

debian apt 更改阿里源
1. 备份文件 cp /etc/apt/sources.list /etc/apt/sources.list.bak 2. 更改 sources.list文件内容为: deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb htt…...

Spring Cloud | “微服务“ 架构 与 Spring Cloud
“微服务” 架构 与 Spring Cloud 目录: "微服务" 架构 与 Spring Cloud1. 认识架构"单体" 架构"SOA" 架构"微服务" 架构 2. "微服务架构" 的功能 :① 微服务架构的 "自动化部署"② 服务 "集中化管理"③…...

win10禁止自动更新的终极方法
添加注册表值 1.运行,输入regedit 2.打开注册表编辑器依次进入以下路径“计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings”。 3.在Settings项中,新建DWORD(32位)值(D),重命名为以下命名“Fl…...

笨方法自学python(二)-注释
注释和#号 程序里的注释是很重要的。它们可以用自然语言告诉你某段代码的功能是什么。在你想要临时移除一段代码时,你还可以用注解的方式将这段代码临时禁用。 # A comment, this is so you can read your program later. # Anything after the # is ignored by py…...

wireshark的安装使用及相关UDP、TCP、 ARP
初步了解: 进入wireshark后如图: 从图中可以看到很多网络连接在操作的时候我们需要监测哪些 我们可以直接在本地的运行框中输入ipconfig来查看 如图: 从以上图片中我们可以清楚地看到哪些网络连接已经连接的我们只需要按需监测他们即可 但…...

【软考】模拟考卷错题本2024-05-11
1 设计模式- 适配器模式 基本上上述的图解已经涵盖了绝大多数主流的设计模式和其特点。理解记忆下即可,这里对下午的考题也有帮助的。 2 计算机组成原理 cpu 访问速度 这个真的是憨憨咯~看到内存就选内存,题目都没审好。这里的速度比cpu内部的要比外部的…...

VMware虚拟机提示内存不足
VMware虚拟机,k8s集群搭建内存不足的问题 疑问:我的电脑是8G8G双通道的内存,当我在搭建k8s集群时给master-2G内存,node1-3G内存,node2-3G内存; 当依次打开虚拟机到node2时VM提示“物理内存不足,…...

视频批量剪辑指南:一键合并视频并添加背景音乐,高效便捷
在数字化时代,视频剪辑已经成为了一项常见且重要的技能。无论是制作家庭影片、工作展示还是社交媒体内容,掌握高效的视频剪辑技巧都能极大地提升我们的工作效率和创作质量。本文将为您介绍云炫AI智剪中高效的视频批量剪辑方法,让您能够一键合…...

讲讲C++四种类型转换
在C中,类型转换(或称为类型转换运算符)是用来将一个数据类型转换为另一个数据类型的机制。C提供了四种类型转换:静态类型转换(Static Cast)、动态类型转换(Dynamic Cast)、重新解释类…...

探索LLM在广告领域的应用——大语言模型的新商业模式和新个性化广告的潜力
概述 在网络搜索引擎的领域中,广告不仅仅是一个补充元素,而是构成了数字体验的核心部分。随着互联网经济的蓬勃发展,广告市场的规模已经达到了数万亿美元,并且还在持续扩张。广告的经济价值不断上升,它已经成为支撑大…...

MBR与GPT分区表
文章目录 MBR分区表MBR分区表结构MBR分区表项查看U盘的分区表信息查看系统中所有磁盘的分区类型获取分区表信息 GPT分区表保护性MBRGPT分区表头格式GPT分区表项格式分区类型分区属性分区表项内容 MBR分区表 CHS :磁头(Heads)、柱面(Cylinder…...

OGG几何内核开发-BRepAlgoAPI_Fuse与BRep_Builder.MakeCompound比较
最近在与同事讨论BRepAlgoAPI_Fuse与BRep_Builder.MakeCompound有什么区别。 一、从直觉上来说,BRepAlgoAPI_Fuse会对两个实体相交处理,相交的部分会重新的生成相关的曲面。而BRep_Builder.MakeCompound仅仅是把两个实体组合成一个新的实体,…...

C语言 | Leetcode C语言题解之第67题二进制求和
题目: 题解: void reserve(char* s) {int len strlen(s);for (int i 0; i < len / 2; i) {char t s[i];s[i] s[len - i - 1], s[len - i - 1] t;} }char* addBinary(char* a, char* b) {reserve(a);reserve(b);int len_a strlen(a), len_b st…...

油泼辣子在食品类别可以申请成商标不!
前阵韩国人在美国申请“chili crunch”油泼辣子作为商标,还准备禁止华人餐馆使用投诉侵权并索赔,普推知产老杨在USPTO上面检索发现,这个人申请的主要是30类方便食品的调味品,商标分类是全球通用的。 商标名称不能申请本类所属的通…...

深入探索MySQL视图
前言 在数据库的世界里,MySQL视图作为数据抽象的一把利剑,为我们提供了一种灵活而高效的方式来管理和查询数据。它不仅能够简化复杂的查询逻辑,还能在不改动底层数据结构的前提下,实现数据的定制化展示与访问控制。本文旨在深入解…...

C语言逻辑语句
叠甲:以下文章主要是依靠我的实际编码学习中总结出来的经验之谈,求逻辑自洽,不能百分百保证正确,有错误、未定义、不合适的内容请尽情指出! 文章目录 1.何为控制语句2.控制语句-分支语句2.1.if2.2.switch 3.控制语句-循…...

C++青少年简明教程:基础知识
C青少年简明教程:基础知识 电脑程序设计(Computer programming),或称程序设计(programming),是给出解决特定问题程序的过程,程序设计往往以某种程序设计语言为工具,给出这…...

[AutoSar]lauterbach_001_ORTI_CPUload_Trace
目录 关键词平台说明一、ORTI概述二、ORTI文件的生成三、ORTI文件的导入四、Trace 功能4.1 Trace 功能菜单介绍4.2 Trace功能的配置4.3 Trace MCDS 设置4.4 Task Switches断点的设置4.5 Trace 数据的录取4.6 CPU 负载和Task调度的查看 关键词 嵌入式、C语言、autosar、OS、BSW…...

课程设计 大学生竞赛系统
课程设计 大学生竞赛系统 wx:help-assignment 学生用户: wx:help-assignment 首页:推荐一些竞赛,热门活动等; 广场:用户可以通过广场来发表动态,同时也可以查看别人发布的动态,并且可以 关注…...

优先队列全面讲解
主题: 优先队列是一种非常有用的数据结构,它让你能够管理一组数据,使得每次访问或移除数据时,总是得到当前集合中优先级最高(或最低)的那个元素。这个特性让优先队列非常适用于需要快速访问集合中最重要元…...

即插即用篇 | YOLOv8 引入多光谱通道注意力 | 频率领域中的通道注意力网络
本改进已集成到 YOLOv8-Magic 框架。 注意力机制,尤其是通道注意力,在计算机视觉领域取得了巨大成功。许多工作聚焦于如何设计高效的通道注意力机制,同时忽略了一个基本问题,即通道注意力机制使用标量来表示通道,这很困难,因为会造成大量信息的丢失。在这项工作中,我们从…...