From 264d01e9b98831a5b4ead697b029112c81ce1e05 Mon Sep 17 00:00:00 2001 From: zhanghuan <1262329256@qq.com> Date: Mon, 15 Sep 2025 17:26:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E7=A6=BB=E7=BA=BF=E7=9A=84=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=EF=BC=88=E5=8F=AF=E6=89=A9=E5=85=85=E6=95=B0=E6=8D=AE=E9=9B=86?= =?UTF-8?q?=EF=BC=89=EF=BC=8C=E5=B0=86=E5=9B=BE=E7=89=87=E5=8E=8B=E7=BC=A9?= =?UTF-8?q?=E4=BB=8E32*32=EF=BC=8C=E8=B0=83=E6=95=B4=E4=B8=BA224*224.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- classifier/food_classifier_app.py | 2 +- data_expansion/data_expansion.py | 50 +++++++++++++++++++++++++++++++ net/food_net.py | 21 ++++++++----- settings/settings.py | 3 +- train/train_food_classifier.py | 18 ++++++++--- 5 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 data_expansion/data_expansion.py diff --git a/classifier/food_classifier_app.py b/classifier/food_classifier_app.py index c70591f..8031dcd 100644 --- a/classifier/food_classifier_app.py +++ b/classifier/food_classifier_app.py @@ -30,7 +30,7 @@ class FoodClassifierApp: self.root.geometry("1400x800") # 食物类别(根据您的数据集) - self.food_classes = ["回锅肉", "西红柿鸡蛋","麻辣小面"] + self.food_classes = ['回锅肉', '炒细面', '西红柿鸡蛋', '麻辣小面'] # 设备设置 self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") diff --git a/data_expansion/data_expansion.py b/data_expansion/data_expansion.py new file mode 100644 index 0000000..7d28bc5 --- /dev/null +++ b/data_expansion/data_expansion.py @@ -0,0 +1,50 @@ +import os +import random +from PIL import Image +from torchvision import transforms + +# 原始数据目录 +input_root = "../dataset/train" + +# 增强后保存目录 +output_root = "../dataset/train_aug" + +# 每类目标张数 +target_num = 500 + +# 定义数据增强 +transform = transforms.Compose([ + transforms.RandomHorizontalFlip(p=0.5), + transforms.RandomRotation(15), + transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1, hue=0.02), + transforms.RandomResizedCrop(size=(224, 224), scale=(0.8, 1.0)), +]) + +os.makedirs(output_root, exist_ok=True) + +# 遍历每个类别文件夹 +for class_name in os.listdir(input_root): + input_dir = os.path.join(input_root, class_name) + output_dir = os.path.join(output_root, class_name) + os.makedirs(output_dir, exist_ok=True) + + # 读取类别下所有图片路径 + img_files = [f for f in os.listdir(input_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg'))] + img_paths = [os.path.join(input_dir, f) for f in img_files] + + print(f"类别 {class_name} 原始图片数: {len(img_paths)}") + + count = 0 + while count < target_num: + img_path = random.choice(img_paths) + img = Image.open(img_path).convert("RGB") + # 生成增强图 + aug_img = transform(img) + # 保存 + save_path = os.path.join(output_dir, f"aug_{count:03d}.jpg") + aug_img.save(save_path) + count += 1 + + print(f"类别 {class_name} 已扩充到 {target_num} 张,保存于 {output_dir}") + +print("✅ 数据增强完成!") diff --git a/net/food_net.py b/net/food_net.py index 3bd35df..a285fd3 100644 --- a/net/food_net.py +++ b/net/food_net.py @@ -17,9 +17,9 @@ class FoodCNN(nn.Module): super(FoodCNN, self).__init__() self.use_internal_preprocess = use_internal_preprocess - # 图片预处理变换(仅在推理时使用) + # 图片预处理变换(仅在推理时使用)(32)改为(224) self.preprocess = transforms.Compose([ - transforms.Resize((32, 32),interpolation=transforms.InterpolationMode.BILINEAR), + transforms.Resize((224, 224),interpolation=transforms.InterpolationMode.BILINEAR), transforms.ToTensor(), transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) ]) @@ -49,7 +49,8 @@ class FoodCNN(nn.Module): self.dropout3 = nn.Dropout2d(0.25) # 全连接层 - self.fc1 = nn.Linear(128 * 4 * 4, 512) + # self.fc1 = nn.Linear(128 * 4 * 4, 512) + self.fc1 = nn.Linear(128 * 28 * 28, 512) self.dropout4 = nn.Dropout(0.5) self.fc2 = nn.Linear(512, settings.NUM_CLASSES) @@ -76,8 +77,9 @@ class FoodCNN(nn.Module): tensor = tensor.unsqueeze(0) # 使用与移动端相同的插值方法缩放到32x32 - tensor = F.interpolate(tensor, size=(32, 32), mode='bilinear', align_corners=False) - + # tensor = F.interpolate(tensor, size=(32, 32), mode='bilinear', align_corners=False) + tensor = F.interpolate(tensor, size=(224, 224), mode='bilinear', align_corners=False) + # ImageNet标准化 mean = torch.tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1) std = torch.tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1) @@ -100,7 +102,8 @@ class FoodCNN(nn.Module): # x = x.float() / 255.0 # 缩放到 32x32 - x = F.interpolate(x, size=(32, 32), mode='bilinear', align_corners=False) + # x = F.interpolate(x, size=(32, 32), mode='bilinear', align_corners=False) + x = F.interpolate(x, size=(224, 224), mode='bilinear', align_corners=False) # 缩放到 32x32 - 使用 align_corners=True 来匹配 PIL 的默认行为 # x = F.interpolate(x, size=(32, 32), mode='bilinear', align_corners=True) @@ -140,8 +143,10 @@ class FoodCNN(nn.Module): x = self.dropout3(x) # 展平 - x = x.view(-1, 128 * 4 * 4) - + # x = x.view(-1, 128 * 4 * 4) + x = x.view(-1, 128 * 28 * 28) + # x = x.view(-1, 65536) + # 全连接层 x = F.relu(self.fc1(x)) x = self.dropout4(x) diff --git a/settings/settings.py b/settings/settings.py index afc3451..01b89af 100644 --- a/settings/settings.py +++ b/settings/settings.py @@ -15,7 +15,7 @@ VAL_DATA_DIR = os.path.join(DATASET_DIR, 'val') TEST_DATA_DIR = os.path.join(DATASET_DIR, 'test') # 模型保存路径 -MODEL_DIR = os.path.join(BASE_DIR, 'model', '12') +MODEL_DIR = os.path.join(BASE_DIR, 'model', '15') BEST_MODEL_PATH = os.path.join(MODEL_DIR, 'best_food_model.pth') TRAINING_CURVES_PATH = os.path.join(MODEL_DIR, 'training_curves.png') TRAINING_RESULTS_PATH = os.path.join(MODEL_DIR, 'training_results.txt') @@ -26,6 +26,7 @@ INFERENCE_BEST_MODEL_PATH = BEST_MODEL_PATH # 训练参数 NUM_EPOCHS = 100 BATCH_SIZE = 32 +# BATCH_SIZE = 128 LEARNING_RATE = 0.001 WEIGHT_DECAY = 1e-4 diff --git a/train/train_food_classifier.py b/train/train_food_classifier.py index 54dc714..63e7d8c 100644 --- a/train/train_food_classifier.py +++ b/train/train_food_classifier.py @@ -30,17 +30,27 @@ print(f"使用设备: {device}") # 数据预处理 - 必须包含Resize以保证batch中tensor尺寸一致,只有Normalize由模型内部完成 +# 把32,32换成224,224 +# transform_train = transforms.Compose([ +# transforms.Resize((224, 224)), +# transforms.RandomHorizontalFlip(p=0.5), +# transforms.RandomRotation(10), +# transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1), +# transforms.ToTensor(), +# # 注意:只有Normalize由模型内部处理 +# ]) transform_train = transforms.Compose([ - transforms.Resize((32, 32)), # 必须保留,确保batch中tensor尺寸一致 + transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p=0.5), transforms.RandomRotation(10), - transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1), + transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1, hue=0.02), transforms.ToTensor(), # 注意:只有Normalize由模型内部处理 ]) +# 把缩放成32*32,修改为224*224 transform_test = transforms.Compose([ - transforms.Resize((32, 32)), # 必须保留,确保batch中tensor尺寸一致 + transforms.Resize((224, 224)), # 必须保留,确保batch中tensor尺寸一致 transforms.ToTensor(), # 注意:只有Normalize由模型内部处理 ]) @@ -129,7 +139,7 @@ def test(model, test_loader, device, class_names): }) print(f'\n测试集总体准确率: {100.*correct/total:.2f}%') - for i in range(3): + for i in range(settings.NUM_CLASSES): if class_total[i] > 0: print(f'{class_names[i]} 准确率: {100.*class_correct[i]/class_total[i]:.2f}%')