{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":7327,"databundleVersionId":861871,"sourceType":"competition"}],"dockerImageVersionId":30627,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# 读取图片文件\nimport os\n# 拷贝模型参数\nimport copy\nimport pandas as pd\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport torchvision.datasets\nimport torchvision.transforms as transforms\nimport torchvision.transforms.functional as TF\nimport torchvision.models as models\nfrom torchvision.transforms import ToPILImage\nfrom tqdm.auto import tqdm\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.optim.lr_scheduler import CosineAnnealingWarmRestarts,ExponentialLR\nfrom sklearn.model_selection import train_test_split\nfrom PIL import Image\nimport cv2\nimport albumentations\nfrom albumentations.pytorch.transforms import ToTensorV2\nimport matplotlib.pyplot as plt\nimport torchvision.utils as vutils\nfrom transformers import ViTForImageClassification","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-01-07T08:45:46.100232Z","iopub.execute_input":"2024-01-07T08:45:46.100604Z","iopub.status.idle":"2024-01-07T08:45:54.381843Z","shell.execute_reply.started":"2024-01-07T08:45:46.100569Z","shell.execute_reply":"2024-01-07T08:45:54.381028Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !pip install efficientnet_pytorch","metadata":{"execution":{"iopub.status.busy":"2024-01-07T08:45:54.382838Z","iopub.execute_input":"2024-01-07T08:45:54.383246Z","iopub.status.idle":"2024-01-07T08:45:54.387296Z","shell.execute_reply.started":"2024-01-07T08:45:54.383221Z","shell.execute_reply":"2024-01-07T08:45:54.386447Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data = pd.read_csv('/kaggle/input/dog-breed-identification/labels.csv')\nlabels = sorted(list(set(list(train_data['breed']))))\nlabels_num = []\nfor i in range(len(train_data)):\n    labels_num.append(labels.index(train_data['breed'][i]))\ntrain_data['number'] = labels_num\ntrain_data.shape","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-01-07T08:45:54.388243Z","iopub.execute_input":"2024-01-07T08:45:54.388526Z","iopub.status.idle":"2024-01-07T08:45:54.570597Z","shell.execute_reply.started":"2024-01-07T08:45:54.388503Z","shell.execute_reply":"2024-01-07T08:45:54.569685Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 创建test的pandas对象\nfile_names = sorted(os.listdir('/kaggle/input/dog-breed-identification/test'))\nfile_names = [name[:-4] for name in file_names]\ntest_data = pd.DataFrame({'id':file_names})\ntest_data.head()","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-01-07T08:45:54.573661Z","iopub.execute_input":"2024-01-07T08:45:54.574014Z","iopub.status.idle":"2024-01-07T08:45:55.037380Z","shell.execute_reply.started":"2024-01-07T08:45:54.573982Z","shell.execute_reply":"2024-01-07T08:45:55.036506Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# transforms_train = albumentations.Compose(\n#     [\n#         albumentations.Resize(224, 224),\n#         albumentations.HorizontalFlip(p=0.5),\n#         albumentations.VerticalFlip(p=0.5),\n#         albumentations.Rotate(limit=180, p=0.7),\n#         albumentations.RandomBrightnessContrast(),\n#         albumentations.ShiftScaleRotate(\n#             shift_limit=0.25, scale_limit=0.1, rotate_limit=0\n#         ),\n#         albumentations.Normalize(\n#             [0.485, 0.456, 0.406], [0.229, 0.224, 0.225],\n#             max_pixel_value=255.0, always_apply=True\n#         ),\n#         ToTensorV2(p=1.0),\n#     ]\n# )\n# transforms_test = albumentations.Compose(\n#         [\n#             albumentations.Resize(224, 224),\n#             albumentations.Normalize(\n#                 [0.485, 0.456, 0.406], [0.229, 0.224, 0.225],\n#                 max_pixel_value=255.0, always_apply=True\n#             ),\n#             ToTensorV2(p=1.0)\n#         ]\n#     )\ntransforms_train =transforms.Compose([\n#         ToPILImage(),\n        transforms.RandomResizedCrop(224),\n        transforms.RandomHorizontalFlip(),\n        transforms.ToTensor(),\n        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n    ])\ntransforms_test = transforms.Compose([\n#         ToPILImage(),\n        transforms.Resize(256),\n        transforms.CenterCrop(224),\n        transforms.ToTensor(),\n        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n    ])","metadata":{"execution":{"iopub.status.busy":"2024-01-07T08:45:55.038689Z","iopub.execute_input":"2024-01-07T08:45:55.039132Z","iopub.status.idle":"2024-01-07T08:45:55.048264Z","shell.execute_reply.started":"2024-01-07T08:45:55.039097Z","shell.execute_reply":"2024-01-07T08:45:55.047414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#自定义数据集,继承Dataset,并且在该过程中就已经将图像进行了转换\nclass Dog_Breed(Dataset):\n    def __init__(self, train_csv, transform = None, test = False):\n        super().__init__()\n        self.train_csv = train_csv\n        self.image_path = list(self.train_csv['id']) #图像所在地址记录\n        self.test = test\n        if not self.test:\n            self.label_nums = list(self.train_csv['number']) #图像的标号记录\n        self.transform = transform\n    def __getitem__(self, idx):\n        '''\n        idx : 所需要获取的图像的索引\n        return : image， label\n        '''\n        if self.test:\n            image = Image.open(os.path.join(\"/kaggle/input/dog-breed-identification/test\", self.image_path[idx]+\".jpg\")).convert('RGB')\n        else:\n            image = Image.open(os.path.join(\"/kaggle/input/dog-breed-identification/train\", self.image_path[idx]+\".jpg\")).convert('RGB')\n        \n        if(self.transform != None):\n            image = self.transform(image)\n        if not self.test:\n            label = self.label_nums[idx]\n            return image, label\n        else:\n            return image\n\n    def __len__(self):\n        return len(self.image_path)","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-01-07T08:45:55.049530Z","iopub.execute_input":"2024-01-07T08:45:55.049904Z","iopub.status.idle":"2024-01-07T08:45:55.060208Z","shell.execute_reply.started":"2024-01-07T08:45:55.049873Z","shell.execute_reply":"2024-01-07T08:45:55.059325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_device():\n    return 'cuda' if torch.cuda.is_available() else 'cpu'\ndevice = get_device()\ndevice","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-01-07T08:45:55.061544Z","iopub.execute_input":"2024-01-07T08:45:55.061881Z","iopub.status.idle":"2024-01-07T08:45:55.096643Z","shell.execute_reply.started":"2024-01-07T08:45:55.061850Z","shell.execute_reply":"2024-01-07T08:45:55.095617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 获取数据集\n# 获取各自的数据集\n# 将数据集train_data分为训练集和验证集\ntrain, valid = train_test_split(train_data, test_size=0.2, random_state=i)\ntrainset = Dog_Breed(train, transform = transforms_train)\nvalidset = Dog_Breed(valid, transform = transforms_test)","metadata":{"execution":{"iopub.status.busy":"2024-01-07T08:45:55.097720Z","iopub.execute_input":"2024-01-07T08:45:55.098005Z","iopub.status.idle":"2024-01-07T08:45:55.113248Z","shell.execute_reply.started":"2024-01-07T08:45:55.097981Z","shell.execute_reply":"2024-01-07T08:45:55.112164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 获取各自的数据加载器\ntrain_loader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, drop_last=False)\nvalid_loader = torch.utils.data.DataLoader(validset, batch_size=32, shuffle=False, drop_last=False)","metadata":{"execution":{"iopub.status.busy":"2024-01-07T08:45:55.114569Z","iopub.execute_input":"2024-01-07T08:45:55.114833Z","iopub.status.idle":"2024-01-07T08:45:55.119748Z","shell.execute_reply.started":"2024-01-07T08:45:55.114809Z","shell.execute_reply":"2024-01-07T08:45:55.118838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 定义模型\nclass MyResNet50(nn.Module):\n    def __init__(self, num_classes=120):\n        super(MyResNet50, self).__init__()\n        # 加载预训练的 ResNet-50 模型\n        self.net = models.resnet50(pretrained=True)\n#         for param in self.net.parameters():\n#             param.requires_grad = False\n        # 修改最后一层，将输出特征的数量调整为 num_classes\n        in_features = self.net.fc.in_features\n        # 修改最后一层，将输出特征的数量调整为 num_classes\n        self.net.fc = nn.Linear(in_features, num_classes)\n        \n\n    def forward(self, x):\n        x = self.net(x)\n\n        return x","metadata":{"execution":{"iopub.status.busy":"2024-01-07T08:45:55.120774Z","iopub.execute_input":"2024-01-07T08:45:55.121109Z","iopub.status.idle":"2024-01-07T08:45:55.129080Z","shell.execute_reply.started":"2024-01-07T08:45:55.121079Z","shell.execute_reply":"2024-01-07T08:45:55.128258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class MyViTModel(nn.Module):\n    def __init__(self, num_classes=120):\n        super(MyViTModel, self).__init__()\n        # 加载预训练的 ViT 模型\n        self.model = ViTForImageClassification.from_pretrained(\"google/vit-base-patch16-224\")\n#         self.feature_extractor = ViTFeatureExtractor.from_pretrained(\"google/vit-base-patch16-224-in21k-finetuned-imagenet1k\")\n        # 修改最后一层，将输出特征的数量调整为 num_classes\n        self.model.classifier = nn.Linear(self.model.config.hidden_size, num_classes)\n\n    def forward(self, x):\n        # 使用 ViT 模型进行前向传播\n        outputs = self.model(x)\n        return outputs.logits\n","metadata":{"execution":{"iopub.status.busy":"2024-01-07T08:53:16.591296Z","iopub.execute_input":"2024-01-07T08:53:16.591695Z","iopub.status.idle":"2024-01-07T08:53:16.597934Z","shell.execute_reply.started":"2024-01-07T08:53:16.591665Z","shell.execute_reply":"2024-01-07T08:53:16.596957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_model(model,train_loader, valid_loader,loss,optimizer,epoch,device = torch.device(\"cuda:0\")):\n#     net = torchvision.models.resnet50(pretrained=True)\n#     in_features = net.fc.in_features\n#     net.fc = nn.Linear(in_features, 120)\n    net = model.to(device)\n    best_epoch = 0\n    best_score = 0.0\n    best_model_state = None\n    early_stopping_round = 10\n    losses = []\n\n    scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2, eta_min = 1e-4)\n    # 按照指数衰减的方式更新优化器中的学习率\n    # gamma 参数指定了学习率衰减的因子。在每个 epoch 结束时，学习率将乘以 gamma\n    # verbose=True: 这个参数是可选的。如果设置为 True，则会在每个 epoch 结束时打印学习率的值。\n#     scheduler = ExponentialLR(optimizer, gamma=0.9,verbose=True)\n    for i in range(epoch):\n        acc = 0\n        loss_sum = 0\n        net.train()\n        for x, y in tqdm(train_loader):\n            optimizer.zero_grad()\n            # todo 会不会多此一举\n#             x = torch.as_tensor(x, dtype=torch.float)\n            x = x.to(device)\n            y = y.to(device)\n            y_hat = net(x)\n            loss_temp = loss(y_hat, y)\n            loss_sum += loss_temp\n            loss_temp.backward()\n            optimizer.step()\n            acc += torch.sum(y_hat.argmax(dim=1) == y)\n        # 每一个epoch更新一次学习率而不是每一个批次\n        scheduler.step()\n        losses.append(loss_sum.cpu().detach().numpy() / len(train_loader))\n        print( \"epoch: \", i, \"loss=\", loss_sum.item()/(len(train_loader)*train_loader.batch_size), \"训练集准确度=\",(acc/(len(train_loader)*train_loader.batch_size)).item(),end=\"\")\n\n        #每一个epoch验证一次\n        test_acc = 0\n        net.eval()\n        for x, y in tqdm(valid_loader):\n            x = x.to(device)\n#             x = torch.as_tensor(x, dtype=torch.float)\n            y = y.to(device)\n            y_hat = net(x)\n            test_acc += torch.sum(y_hat.argmax(dim=1) == y)\n        print(\"验证集准确度\", (test_acc / (len(valid_loader)*valid_loader.batch_size)).item())\n        if test_acc > best_score:\n            # 保存最好的模型\n            best_model_state = copy.deepcopy(net.state_dict())\n            best_score = test_acc\n            best_epoch = i\n            print('best epoch save!')\n        if i - best_epoch >= early_stopping_round:\n            break\n\n    net.load_state_dict(best_model_state)\n    \n    testset = Dog_Breed(test_data, transform = transforms_test,test = True)\n    # todo 测试集加载器为什么在里面，为什么不在外面？而训练集和验证集加载器在外面？\n    test_loader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False, drop_last=False)\n\n    predictions = []\n    with torch.no_grad():\n        for x in tqdm(test_loader):\n            x = x.to(device)\n            # x = torch.as_tensor(x, dtype=torch.float)\n            y_hat = net(x)\n            # predict = torch.argmax(y_hat,dim=1).reshape(-1)\n            predict = list(y_hat.cpu().detach().numpy())\n            # predict的形状为[64,120]\n            predictions.extend(predict)\n    predictions = np.array(predictions)\n    prediction = torch.tensor(predictions).reshape(-1, 120)\n    return prediction","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-01-07T08:53:16.909809Z","iopub.execute_input":"2024-01-07T08:53:16.910698Z","iopub.status.idle":"2024-01-07T08:53:16.926999Z","shell.execute_reply.started":"2024-01-07T08:53:16.910665Z","shell.execute_reply":"2024-01-07T08:53:16.926151Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn_rate = 0.001\nmomentum = 0.9 \nepoch = 20","metadata":{"execution":{"iopub.status.busy":"2024-01-07T08:53:17.441514Z","iopub.execute_input":"2024-01-07T08:53:17.441863Z","iopub.status.idle":"2024-01-07T08:53:17.446231Z","shell.execute_reply.started":"2024-01-07T08:53:17.441836Z","shell.execute_reply":"2024-01-07T08:53:17.445317Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = MyViTModel()\n# 定义损失函数和优化器\n# 设置为 'mean' 表示计算平均损失值。\nloss = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.model.classifier.parameters(), lr=learn_rate,weight_decay=1e-5)\n# optimizer = optim.SGD(model.parameters(), lr=learn_rate,momentum=momentum,weight_decay=1e-5)\n\nprediction = train_model(model,train_loader, valid_loader,loss,optimizer,epoch,device)\nresult = pd.DataFrame(prediction,columns=labels)\nresult = pd.concat([test_data,result],axis=1)\nresult.to_csv('dog_breed.csv',index=False)","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-01-07T08:54:06.189459Z","iopub.execute_input":"2024-01-07T08:54:06.190100Z","iopub.status.idle":"2024-01-07T09:49:40.785587Z","shell.execute_reply.started":"2024-01-07T08:54:06.190068Z","shell.execute_reply":"2024-01-07T09:49:40.784656Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# todo输出层没有修改为概率，导致很多数字都是负数\ndf = pd.read_csv('/kaggle/working/dog_breed.csv')\ndata = df.iloc[:,1:].values\ntensor_data = torch.tensor(data,dtype=torch.float32)\nprobability = F.softmax(tensor_data,dim=1)\ndf.iloc[:,1:] = probability.numpy()\ndf.to_csv('dog_breed.csv',index=False)","metadata":{"execution":{"iopub.status.busy":"2024-01-07T09:49:48.114020Z","iopub.execute_input":"2024-01-07T09:49:48.114377Z","iopub.status.idle":"2024-01-07T09:49:51.059047Z","shell.execute_reply.started":"2024-01-07T09:49:48.114346Z","shell.execute_reply":"2024-01-07T09:49:51.058018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}