import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from thop import profile
import time
import os
from torch.utils.tensorboard import SummaryWriter
# 设备配置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")
# 数据预处理
def get_cifar100_dataloaders(batch_size=128, resolution=32):
"""获取CIFAR-100数据集加载器"""
transform_train = transforms.Compose([
transforms.Resize((resolution, resolution)),
transforms.RandomCrop(resolution, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.5071, 0.4867, 0.4408), (0.2675, 0.2565, 0.2761))
])
transform_test = transforms.Compose([
transforms.Resize((resolution, resolution)),
transforms.ToTensor(),
transforms.Normalize((0.5071, 0.4867, 0.4408), (0.2675, 0.2565, 0.2761))
])
# 使用用户指定的路径
data_path = r'C:\Users\89373\Desktop\imagenet\cifar-100-python'
train_set = torchvision.datasets.CIFAR100(
root=data_path, train=True, download=False, transform=transform_train)
test_set = torchvision.datasets.CIFAR100(
root=data_path, train=False, download=False, transform=transform_test)
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=4, pin_memory=True)
test_loader = DataLoader(test_set, batch_size=batch_size, shuffle=False, num_workers=4, pin_memory=True)
return train_loader, test_loader
# 深度可分离卷积模块
class DepthwiseSeparableConv(nn.Module):
"""深度可分离卷积实现"""
def __init__(self, in_channels, out_channels, stride):
super().__init__()
# 深度卷积 (DW卷积)
self.depthwise = nn.Sequential(
nn.Conv2d(in_channels, in_channels, 3, stride, 1, groups=in_channels, bias=False),
nn.BatchNorm2d(in_channels),
nn.ReLU6(inplace=True)
)
# 逐点卷积 (1x1卷积)
self.pointwise = nn.Sequential(
nn.Conv2d(in_channels, out_channels, 1, 1, 0, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU6(inplace=True)
)
def forward(self, x):
x = self.depthwise(x)
return self.pointwise(x)
# 针对CIFAR-100调整的MobileNet v1
class MobileNetV1_CIFAR100(nn.Module):
"""针对CIFAR-100调整的MobileNet v1架构"""
def __init__(self, alpha=1.0, num_classes=100):
super().__init__()
def c(channels): return int(channels * alpha) # 宽度乘数α控制通道数
# 针对32x32输入调整的网络结构
self.features = nn.Sequential(
# 初始卷积层 (调整stride为1以适应小尺寸图像)
nn.Conv2d(3, c(32), 3, 1, 1, bias=False),
nn.BatchNorm2d(c(32)),
nn.ReLU6(inplace=True),
# 深度可分离卷积层 (减少下采样次数)
DepthwiseSeparableConv(c(32), c(64), 1),
DepthwiseSeparableConv(c(64), c(128), 2),
DepthwiseSeparableConv(c(128), c(128), 1),
DepthwiseSeparableConv(c(128), c(256), 2),
DepthwiseSeparableConv(c(256), c(256), 1),
DepthwiseSeparableConv(c(256), c(512), 1), # 调整为stride=1
*[DepthwiseSeparableConv(c(512), c(512), 1) for _ in range(2)], # 减少层数
DepthwiseSeparableConv(c(512), c(1024), 2),
DepthwiseSeparableConv(c(1024), c(1024), 1),
nn.AdaptiveAvgPool2d(1)
)
self.classifier = nn.Linear(c(1024), num_classes)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1)
return self.classifier(x)
# 标准卷积网络用于比较
class StandardConvNet(nn.Module):
"""标准卷积网络用于与深度可分离卷积比较"""
def __init__(self, num_classes=100):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 32, 3, 1, 1, bias=False),
nn.BatchNorm2d(32),
nn.ReLU6(inplace=True),
nn.Conv2d(32, 64, 3, 1, 1, bias=False),
nn.BatchNorm2d(64),
nn.ReLU6(inplace=True),
nn.Conv2d(64, 128, 3, 2, 1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU6(inplace=True),
nn.Conv2d(128, 128, 3, 1, 1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU6(inplace=True),
nn.Conv2d(128, 256, 3, 2, 1, bias=False),
nn.BatchNorm2d(256),
nn.ReLU6(inplace=True),
nn.Conv2d(256, 256, 3, 1, 1, bias=False),
nn.BatchNorm2d(256),
nn.ReLU6(inplace=True),
nn.Conv2d(256, 512, 3, 1, 1, bias=False),
nn.BatchNorm2d(512),
nn.ReLU6(inplace=True),
nn.Conv2d(512, 512, 3, 1, 1, bias=False),
nn.BatchNorm2d(512),
nn.ReLU6(inplace=True),
nn.Conv2d(512, 1024, 3, 2, 1, bias=False),
nn.BatchNorm2d(1024),
nn.ReLU6(inplace=True),
nn.Conv2d(1024, 1024, 3, 1, 1, bias=False),
nn.BatchNorm2d(1024),
nn.ReLU6(inplace=True),
nn.AdaptiveAvgPool2d(1)
)
self.classifier = nn.Linear(1024, num_classes)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1)
return self.classifier(x)
# 训练函数
def train_model(model, train_loader, test_loader, epochs=100, lr=0.01, experiment_name="baseline"):
"""训练并评估模型"""
# 创建日志目录
log_dir = f"logs/{experiment_name}_{time.strftime('%Y%m%d_%H%M%S')}"
os.makedirs(log_dir, exist_ok=True)
writer = SummaryWriter(log_dir)
model = model.to(device)
optimizer = optim.RMSprop(model.parameters(), lr=lr, momentum=0.9, weight_decay=1e-5)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'max', patience=5, factor=0.5, verbose=True)
criterion = nn.CrossEntropyLoss()
best_acc = 0.0
history = {'train_loss': [], 'test_acc': []}
for epoch in range(epochs):
model.train()
running_loss = 0.0
for i, (images, labels) in enumerate(train_loader):
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i + 1) % 100 == 0:
print(f'Epoch [{epoch + 1}/{epochs}], Step [{i + 1}/{len(train_loader)}], Loss: {loss.item():.4f}')
# 计算平均训练损失
avg_train_loss = running_loss / len(train_loader)
history['train_loss'].append(avg_train_loss)
writer.add_scalar('Loss/train', avg_train_loss, epoch)
# 验证评估
model.eval()
total, correct = 0, 0
with torch.no_grad():
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, preds = torch.max(outputs, 1)
total += labels.size(0)
correct += (preds == labels).sum().item()
acc = 100 * correct / total
history['test_acc'].append(acc)
writer.add_scalar('Accuracy/test', acc, epoch)
print(f'Epoch {epoch + 1}/{epochs} | Train Loss: {avg_train_loss:.4f} | Test Acc: {acc:.2f}%')
# 更新学习率
scheduler.step(acc)
# 保存最佳模型
if acc > best_acc:
best_acc = acc
torch.save(model.state_dict(), f"{log_dir}/best_model.pth")
writer.close()
return best_acc, history
# 评估函数
def evaluate_model(model, test_loader):
"""评估模型性能"""
model.eval()
total, correct = 0, 0
with torch.no_grad():
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, preds = torch.max(outputs, 1)
total += labels.size(0)
correct += (preds == labels).sum().item()
return 100 * correct / total
# 计算模型复杂度和性能
def calculate_model_stats(model, input_size=(1, 3, 32, 32)):
"""计算模型参数和FLOPs"""
input_tensor = torch.randn(input_size).to(device)
model = model.to(device)
# 计算FLOPs和参数
flops, params = profile(model, inputs=(input_tensor,), verbose=False)
flops_m = flops / 1e6 # 百万FLOPs
params_m = params / 1e6 # 百万参数
return flops_m, params_m
# 生成实验结果表格
def generate_results_table():
"""生成论文中的实验表格"""
results = []
train_loader, test_loader = get_cifar100_dataloaders(resolution=32)
# 实验1: 标准卷积 vs 深度可分离卷积
print("\n实验1: 标准卷积 vs 深度可分离卷积")
# 标准卷积模型
std_model = StandardConvNet()
std_flops, std_params = calculate_model_stats(std_model)
std_acc = train_model(std_model, train_loader, test_loader, epochs=50, experiment_name="std_conv")[0]
# 深度可分离卷积模型
ds_model = MobileNetV1_CIFAR100(alpha=1.0)
ds_flops, ds_params = calculate_model_stats(ds_model)
ds_acc = train_model(ds_model, train_loader, test_loader, epochs=50, experiment_name="ds_conv")[0]
results.append({
'实验': '卷积类型比较',
'模型': '标准卷积',
'准确率(%)': std_acc,
'参数(M)': f'{std_params:.2f}',
'FLOPs(M)': f'{std_flops:.1f}'
})
results.append({
'实验': '卷积类型比较',
'模型': '深度可分离',
'准确率(%)': ds_acc,
'参数(M)': f'{ds_params:.2f}',
'FLOPs(M)': f'{ds_flops:.1f}'
})
# 实验2: 宽度乘数α的影响
print("\n实验2: 宽度乘数α的影响")
for alpha in [1.0, 0.75, 0.5, 0.25]:
model = MobileNetV1_CIFAR100(alpha=alpha)
flops, params = calculate_model_stats(model)
# 实际训练获取准确率
acc = train_model(model, train_loader, test_loader, epochs=50,
experiment_name=f"alpha_{alpha}")[0]
results.append({
'实验': '宽度乘数α',
'模型': f'α={alpha}',
'准确率(%)': acc,
'参数(M)': f'{params:.2f}',
'FLOPs(M)': f'{flops:.1f}'
})
# 实验3: 分辨率乘数β的影响
print("\n实验3: 分辨率乘数β的影响")
base_model = MobileNetV1_CIFAR100(alpha=1.0)
for resolution in [32, 24, 16]:
# 加载不同分辨率的数据
res_train_loader, res_test_loader = get_cifar100_dataloaders(resolution=resolution)
# 创建新模型实例
model = MobileNetV1_CIFAR100(alpha=1.0)
flops, params = calculate_model_stats(model, input_size=(1, 3, resolution, resolution))
# 实际训练获取准确率
acc = train_model(model, res_train_loader, res_test_loader, epochs=50,
experiment_name=f"res_{resolution}")[0]
results.append({
'实验': '分辨率乘数β',
'模型': f'β={resolution}',
'准确率(%)': acc,
'参数(M)': '1.00', # 参数数量不变
'FLOPs(M)': f'{flops:.1f}'
})
return pd.DataFrame(results)
# 可视化实验结果
def visualize_results(results_df):
"""可视化实验结果"""
plt.figure(figsize=(15, 10))
# 卷积类型比较
plt.subplot(2, 2, 1)
conv_df = results_df[results_df['实验'] == '卷积类型比较']
plt.bar(conv_df['模型'], conv_df['准确率(%)'], color=['blue', 'green'])
plt.title('卷积类型比较 - 准确率')
plt.ylabel('准确率(%)')
# 宽度乘数影响
plt.subplot(2, 2, 2)
alpha_df = results_df[results_df['实验'] == '宽度乘数α']
plt.plot(alpha_df['模型'], alpha_df['准确率(%)'], 'o-', label='准确率')
plt.plot(alpha_df['模型'], alpha_df['参数(M)'].astype(float), 's--', label='参数(M)')
plt.plot(alpha_df['模型'], alpha_df['FLOPs(M)'].astype(float), 'd-.', label='FLOPs(M)')
plt.title('宽度乘数α的影响')
plt.legend()
# 分辨率乘数影响
plt.subplot(2, 2, 3)
res_df = results_df[results_df['实验'] == '分辨率乘数β']
plt.plot(res_df['模型'], res_df['准确率(%)'], 'o-', label='准确率')
plt.plot(res_df['模型'], res_df['FLOPs(M)'].astype(float), 's--', label='FLOPs(M)')
plt.title('分辨率乘数β的影响')
plt.legend()
# 准确率-FLOPs权衡
plt.subplot(2, 2, 4)
plt.scatter(results_df['FLOPs(M)'].astype(float), results_df['准确率(%)'], s=100)
for i, row in results_df.iterrows():
plt.annotate(row['模型'], (float(row['FLOPs(M)']), row['准确率(%)']),
xytext=(5, 5), textcoords='offset points')
plt.xlabel('FLOPs(M)')
plt.ylabel('准确率(%)')
plt.title('准确率-FLOPs权衡')
plt.tight_layout()
plt.savefig('mobilenet_cifar100_results.png')
plt.show()
# 主函数
def main():
# 生成结果表格
results_df = generate_results_table()
# 保存结果
results_df.to_csv('mobilenet_cifar100_results.csv', index=False)
print(results_df)
# 可视化结果
visualize_results(results_df)
print("实验完成!结果已保存到mobilenet_cifar100_results.csv和mobilenet_cifar100_results.png")
if __name__ == '__main__':
main()
D:\anaconda\python.exe D:\杨杏丽作业\pythonProject\ImageNet.py
2025-06-18 10:19:16.836858: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2025-06-18 10:19:18.662333: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
使用设备: cpu
Traceback (most recent call last):
File "D:\杨杏丽作业\pythonProject\ImageNet.py", line 399, in <module>
main()
File "D:\杨杏丽作业\pythonProject\ImageNet.py", line 386, in main
results_df = generate_results_table()
^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\杨杏丽作业\pythonProject\ImageNet.py", line 264, in generate_results_table
train_loader, test_loader = get_cifar100_dataloaders(resolution=32)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\杨杏丽作业\pythonProject\ImageNet.py", line 40, in get_cifar100_dataloaders
train_set = torchvision.datasets.CIFAR100(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda\Lib\site-packages\torchvision\datasets\cifar.py", line 69, in __init__
raise RuntimeError("Dataset not found or corrupted. You can use download=True to download it")
RuntimeError: Dataset not found or corrupted. You can use download=True to download it
进程已结束,退出代码为 1
最新发布