{"id":9474,"date":"2025-11-07T14:22:12","date_gmt":"2025-11-07T06:22:12","guid":{"rendered":"\/?p=9474"},"modified":"2025-11-11T16:34:34","modified_gmt":"2025-11-11T08:34:34","slug":"%e4%bd%bf%e7%94%a8-visiontransformervit-finetune-%e8%ae%ad%e7%bb%83%e9%a9%be%e9%a9%b6%e5%91%98%e8%a1%8c%e4%b8%ba%e7%8a%b6%e6%80%81%e8%af%86%e5%88%ab%e6%a8%a1%e5%9e%8b","status":"publish","type":"post","link":"\/?p=9474","title":{"rendered":"\u4f7f\u7528 VisionTransformer(VIT) FineTune \u8bad\u7ec3\u9a7e\u9a76\u5458\u884c\u4e3a\u72b6\u6001\u8bc6\u522b\u6a21\u578b"},"content":{"rendered":"<h2>\u4e00\u3001VisionTransformer(VIT) \u4ecb\u7ecd<\/h2>\n<p>\u5927\u6a21\u578b\u5df2\u7ecf\u6210\u4e3a\u4eba\u5de5\u667a\u80fd\u9886\u57df\u7684\u70ed\u95e8\u8bdd\u9898\u3002\u5728\u8fd9\u80a1\u70ed\u6f6e\u4e2d\uff0c\u5927\u6a21\u578b\u7684\u6838\u5fc3\u7ed3\u6784 <code>Transformer<\/code> \u4e5f\u518d\u6b21\u8131\u9896\u800c\u51fa\u8bc1\u660e\u4e86\u5176\u5f3a\u5927\u7684\u80fd\u529b\u548c\u5e7f\u6cdb\u7684\u5e94\u7528\u524d\u666f\u3002 <code>Transformer<\/code> \u81ea <code>2017<\/code>\u5e74\u7531 <code>Google<\/code>\u63d0\u51fa\u4ee5\u6765\uff0c\u4fbf\u5728 <code>NLP<\/code>\u9886\u57df\u6380\u8d77\u4e86\u4e00\u573a\u9769\u547d\u3002\u76f8\u8f83\u4e8e\u4f20\u7edf\u7684\u5faa\u73af\u795e\u7ecf\u7f51\u7edc\uff08 <code>RNN<\/code>\uff09\u548c\u957f\u77ed\u65f6\u8bb0\u5fc6\u7f51\u7edc\uff08 <code>LSTM<\/code>\uff09\uff0c <code>Transformer<\/code> \u51ed\u501f\u81ea\u6ce8\u610f\u529b\u673a\u5236\u548c\u7aef\u5230\u7aef\u8bad\u7ec3\u65b9\u5f0f\uff0c\u4ee5\u53ca\u5904\u7406\u957f\u8ddd\u79bb\u4f9d\u8d56\u95ee\u9898\u4e0a\u663e\u8457\u7684\u4f18\u52bf\uff0c\u4f7f\u5176\u5728\u591a\u9879 <code>NLP<\/code>\u4efb\u52a1\u4e2d\u90fd\u53d6\u5f97\u4e86\u5353\u8d8a\u8868\u73b0\uff0c\u5e38\u89c1\u6a21\u578b\u4f8b\u5982\uff1a <code>BERT<\/code>\u3001 <code>GPT<\/code> \u7b49\u3002<\/p>\n<p>\u968f\u7740 <code>Transformer<\/code>\u5728 <code>NLP<\/code>\u9886\u57df\u7684\u6210\u529f\uff0c\u6162\u6162\u7684\u4e5f\u5f00\u59cb\u8fdb\u519b\u5230\u4e86 <code>CV<\/code>\u9886\u57df\u3002\u5728 <code>CV<\/code> \u9886\u57df\u4e2d\uff0c\u5377\u79ef\u795e\u7ecf\u7f51\u7edc\uff08 <code>CNN<\/code>\uff09\u4e00\u76f4\u5360\u636e\u4e3b\u5bfc\u5730\u4f4d\u3002\u7136\u800c\uff0c <code>CNN<\/code> \u7684\u5377\u79ef\u64cd\u4f5c\u9650\u5236\u4e86\u5176\u5bf9\u5168\u5c40\u4fe1\u606f\u7684\u6355\u6349\uff0c\u5bfc\u81f4\u5728\u5904\u7406\u590d\u6742\u573a\u666f\u65f6\u6548\u679c\u4e0d\u4f73\u3002\u76f8\u6bd4\u4e4b\u4e0b\uff0c <code>Transformer<\/code> \u80fd\u591f\u66f4\u597d\u5730\u6355\u6349\u957f\u8ddd\u79bb\u4f9d\u8d56\u5173\u7cfb\uff0c\u6709\u52a9\u4e8e\u8bc6\u522b\u56fe\u50cf\u4e2d\u7684\u5168\u5c40\u7279\u5f81\uff0c\u53e6\u5916\uff0c\u81ea\u6ce8\u610f\u529b\u673a\u5236\u4e5f\u80fd\u4f7f\u5f97\u6a21\u578b\u5173\u6ce8\u5230\u4e0d\u540c\u533a\u57df\u7684\u91cd\u8981\u4fe1\u606f\uff0c\u63d0\u9ad8\u7279\u5f81\u63d0\u53d6\u7684\u51c6\u786e\u6027\u3002<\/p>\n<p>\u4f46\u662f\u8981\u60f3 <code>Transformer<\/code> \u5904\u7406\u56fe\u50cf\uff0c\u9996\u9009\u9700\u8981\u8003\u8651\u5982\u4f55\u5c06\u56fe\u50cf\u8f6c\u4e3a\u5e8f\u5217\u6570\u636e\uff0c\u56e0\u4e3a <code>CNN<\/code> \u7684\u8f93\u5165\u901a\u5e38\u662f\u4e00\u4e2a\u56db\u7ef4\u5f20\u91cf\uff0c\u5176\u7ef4\u5ea6\u901a\u5e38\u8868\u793a\u4e3a <code>[\u6279\u6b21\u5927\u5c0f,\u9ad8\u5ea6,\u5bbd\u5ea6,\u901a\u9053\u6570]<\/code>\uff0c\u4e00\u822c\u56fe\u50cf\u4e5f\u662f <code>RGB<\/code>\u4e09\u7ef4\u7684\uff0c\u6240\u4ee5\u53ef\u4ee5\u975e\u5e38\u65b9\u4fbf\u7684\u5904\u7406\u56fe\u50cf\u6570\u636e\u3002\u800c <code>Transformer<\/code> \u7684\u8f93\u5165\u662f\u4e00\u4e2a\u4e09\u7ef4\u5f20\u91cf\uff0c\u5176\u7ef4\u5ea6\u8868\u793a\u4e3a <code>[\u6279\u6b21\u5927\u5c0f\uff0c\u5e8f\u5217\u957f\u5ea6,\u5d4c\u5165\u7ef4\u5ea6]<\/code>\uff0c\u7ef4\u5ea6\u7684\u4e0d\u540c\u5bfc\u81f4\u4e0d\u80fd\u76f4\u63a5\u5c06\u56fe\u50cf\u4f20\u5165 <code>Transformer<\/code> \u7ed3\u6784 \u3002<\/p>\n<p>\u5bf9\u6b64 <code>VisionTransformer<\/code> ( <code>VIT<\/code>\uff09\u5de7\u5999\u7684\u4f8b\u7528\u4e86 <code>CNN<\/code> \u89e3\u51b3\u4e86\u7ef4\u5ea6\u4e0d\u4e00\u81f4\u7684\u95ee\u9898\uff0c\u6210\u4e3a\u4e86\u5c06 <code>Transformer<\/code> \u67b6\u6784\u5e94\u7528\u4e8e <code>CV<\/code> \u9886\u57df\u7684\u4e00\u79cd\u521b\u65b0\u65b9\u6cd5\uff0c \u4e0b\u9762\u662f <code>VIT<\/code> \u7684\u67b6\u6784\u56fe\uff1a<\/p>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140804708.png\" alt=\"\u56fe\u7247\" \/><\/p>\n<p>\u9996\u5148\uff0c <code>VIT<\/code>\u5c06\u8f93\u5165\u56fe\u50cf\u5206\u5272\u6210\u4e00\u7cfb\u5217\u56fa\u5b9a\u5927\u5c0f\u7684\u56fe\u50cf\u5757(\u5229\u7528 <code>CNN<\/code>)\uff0c\u6bcf\u4e2a\u5757\u5c31\u50cf <code>NLP<\/code>\u4e2d\u7684\u5355\u8bcd\u4e00\u6837\uff0c\u6210\u4e3a\u5e8f\u5217\u4e2d\u7684\u4e00\u4e2a\u5143\u7d20\uff0c\u8fd9\u70b9\u7c7b\u4f3c\u4e8e\u6587\u672c\u6a21\u578b\u4e2d\u7684 <code>Embedding<\/code> \u5c42\u3002\u8fd9\u79cd\u5206\u5272\u65b9\u6cd5\u4f7f\u5f97\u56fe\u50cf\u7684\u5c40\u90e8\u7279\u5f81\u5f97\u4ee5\u4fdd\u7559\uff0c\u5e76\u4e3a\u540e\u7eed\u7684\u5904\u7406\u63d0\u4f9b\u4e86\u57fa\u7840\u3002\u63a5\u7740\uff0c\u4e3a\u4e86\u786e\u4fdd\u6a21\u578b\u80fd\u591f\u7406\u89e3\u56fe\u50cf\u5757\u7684\u7a7a\u95f4\u4f4d\u7f6e\uff0cVIT\u4e3a\u6bcf\u4e2a\u56fe\u50cf\u5757\u6dfb\u52a0\u4e86\u4f4d\u7f6e\u7f16\u7801\uff0c\u8fd9\u4e9b\u7f16\u7801\u662f\u53ef\u5b66\u4e60\u7684\u53c2\u6570\uff0c\u5b83\u4eec\u51c6\u786e\u5730\u6307\u793a\u4e86\u6bcf\u4e2a\u5757\u5728\u539f\u59cb\u56fe\u50cf\u4e2d\u7684\u4f4d\u7f6e\u3002<\/p>\n<p>\u7136\u540e\uff0c\u6bcf\u4e2a\u56fe\u50cf\u5757\u88ab\u5c55\u5e73\u6210\u4e00\u7ef4\u5411\u91cf\uff0c\u5e76\u901a\u8fc7\u4e00\u4e2a\u7ebf\u6027\u5c42\u8fdb\u884c\u5d4c\u5165\uff0c\u8f6c\u6362\u6210\u9ad8\u7ef4\u5411\u91cf\u3002\u8fd9\u4e2a\u8fc7\u7a0b\u7c7b\u4f3c\u4e8e\u5728\u81ea\u7136\u8bed\u8a00\u5904\u7406\u4e2d\u5c06\u5355\u8bcd\u6620\u5c04\u5230\u8bcd\u5d4c\u5165\u5411\u91cf\u3002\u5b8c\u6210\u5d4c\u5165\u540e\uff0c\u8fd9\u4e9b\u5411\u91cf\u88ab\u9001\u5165\u6807\u51c6\u7684 <code>Transformer<\/code>\u7f16\u7801\u5668\u4e2d\u3002\u7f16\u7801\u5668\u7531\u591a\u4e2a\u81ea\u6ce8\u610f\u529b\u5c42\u548c\u524d\u9988\u7f51\u7edc\u7ec4\u6210\uff0c\u5b83\u4eec\u80fd\u591f\u6355\u6349\u56fe\u50cf\u5757\u4e4b\u95f4\u7684\u590d\u6742\u4ea4\u4e92\u548c\u4f9d\u8d56\u5173\u7cfb\u3002<\/p>\n<p>\u6700\u540e\uff0c <code>VIT<\/code>\u5728 <code>Transformer<\/code>\u7f16\u7801\u5668\u7684\u8f93\u51fa\u4e0a\u6dfb\u52a0\u4e86\u4e00\u4e2a\u5206\u7c7b\u5934\uff0c\u901a\u5e38\u662f\u4e00\u4e2a\u5168\u8fde\u63a5\u5c42\uff0c\u7528\u4e8e\u751f\u6210\u6700\u7ec8\u7684\u5206\u7c7b\u7ed3\u679c\u3002<\/p>\n<p>\u4e0b\u9762\u662f <code>VIT-Base<\/code> \u7684\u636e\u56fe\u7ed3\u6784\uff1a<\/p>\n<pre><code>VisionTransformer(\n  (conv_proj): Conv2d(3, 768, kernel_size=(16, 16), stride=(16, 16))\n  (encoder): Encoder(\n    (dropout): Dropout(p=0.0, inplace=False)\n    (layers): Sequential(\n      (encoder_layer_0): EncoderBlock(\n        (ln_1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)\n        (self_attention): MultiheadAttention(\n          (out_proj): NonDynamicallyQuantizableLinear(in_features=768, out_features=768, bias=True)\n        )\n        (dropout): Dropout(p=0.0, inplace=False)\n        (ln_2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)\n        (mlp): MLPBlock(\n          (0): Linear(in_features=768, out_features=3072, bias=True)\n          (1): GELU(approximate=&#039;none&#039;)\n          (2): Dropout(p=0.0, inplace=False)\n          (3): Linear(in_features=3072, out_features=768, bias=True)\n          (4): Dropout(p=0.0, inplace=False)\n        )\n      )\n      (encoder_layer_1): EncoderBlock(\n        (ln_1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)\n        (self_attention): MultiheadAttention(\n          (out_proj): NonDynamicallyQuantizableLinear(in_features=768, out_features=768, bias=True)\n        )\n        (dropout): Dropout(p=0.0, inplace=False)\n        (ln_2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)\n        (mlp): MLPBlock(\n          (0): Linear(in_features=768, out_features=3072, bias=True)\n          (1): GELU(approximate=&#039;none&#039;)\n          (2): Dropout(p=0.0, inplace=False)\n          (3): Linear(in_features=3072, out_features=768, bias=True)\n          (4): Dropout(p=0.0, inplace=False)\n        )\n      )\n      ...\n      (encoder_layer_11): EncoderBlock(\n        (ln_1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)\n        (self_attention): MultiheadAttention(\n          (out_proj): NonDynamicallyQuantizableLinear(in_features=768, out_features=768, bias=True)\n        )\n        (dropout): Dropout(p=0.0, inplace=False)\n        (ln_2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)\n        (mlp): MLPBlock(\n          (0): Linear(in_features=768, out_features=3072, bias=True)\n          (1): GELU(approximate=&#039;none&#039;)\n          (2): Dropout(p=0.0, inplace=False)\n          (3): Linear(in_features=3072, out_features=768, bias=True)\n          (4): Dropout(p=0.0, inplace=False)\n        )\n      )\n    )\n    (ln): LayerNorm((768,), eps=1e-06, elementwise_affine=True)\n  )\n  (heads): Sequential(\n    (head): Linear(in_features=768, out_features=1000, bias=True)\n  )\n)<\/code><\/pre>\n<p>\u4ece\u7ed3\u6784\u4e2d\u53ef\u4ee5\u770b\u51fa\uff0c\u8f93\u5165\u4e09\u7ef4\u56fe\u50cf\uff0c \u7ecf\u8fc7 <code>(16,16)<\/code> \u7684\u5377\u79ef\u6838\uff0c\u5e76\u4e14\u6b65\u957f\u4e5f\u662f <code>(16,16)<\/code> \uff0c\u5982\u679c\u8f93\u5165\u5927\u5c0f\u4e3a <code>(224,224)<\/code> \uff0c\u5219\u8f93\u51fa\u5c31\u4e3a <code>768<\/code> \u4e2a\u5927\u5c0f\u4e3a <code>(14,14)<\/code> \u7684\u7279\u5f81\u56fe\uff0c\u7136\u540e\u6bcf\u4e2a\u7279\u5f81\u56fe\u5728\u5c55\u5e73\u6210\u4e00\u7ef4\u5411\u91cf\u5c31\u662f <code>(batch\uff0c768\uff0c196)<\/code> \uff0c\u63a5\u7740\u540e\u9762\u5c31\u53ef\u4ee5\u5582\u5165\u5230 <code>Transformer<\/code> \u7ed3\u6784\u4e86\u3002<\/p>\n<p>\u4e0a\u9762\u5bf9 <code>VIT<\/code> \u6709\u4e86\u7b80\u5355\u7684\u4e86\u89e3\u540e\uff0c\u4e0b\u5356\u5f04\u4f7f\u7528 <code>Pytorch<\/code> <code>vit_b_16<\/code> \u6a21\u578b <code>FineTune<\/code> \u8bad\u7ec3\u4e0b <code>Kaggle<\/code> \u6bd4\u8d5b\u4e2d\u7684\u9a7e\u9a76\u5458\u72b6\u6001\u6570\u636e\u96c6\u3002<\/p>\n<p>\u5b9e\u9a8c\u4f7f\u7528\u7684\u4f9d\u8d56\u7248\u672c\u5982\u4e0b\uff1a<\/p>\n<pre><code>torch==1.13.1+cu116\ntorchvision==0.14.1+cu116\ntensorboard==2.17.1\ntensorboard-data-server==0.7.2<\/code><\/pre>\n<h2>\u4e8c\u3001\u51c6\u5907\u6570\u636e\u96c6<\/h2>\n<p>\u9a7e\u9a76\u5458\u72b6\u6001\u6570\u636e\u96c6\u8fd9\u91cc\u4f7f\u7528 <code>Kaggle<\/code> \u6bd4\u8d5b\u7684\u6570\u636e\uff0c\u7531\u4e8e\u5b98\u7f51\u5df2\u7ecf\u6ca1\u529e\u6cd5\u4e0b\u8f7d\u4e86\uff0c\u8fd9\u91cc\u53ef\u4ee5\u5728 \u767e\u5ea6\u7684 <code>aistudio<\/code> \u516c\u5f00\u6570\u636e\u96c6\u4e2d\u4e0b\u8f7d\uff1a<\/p>\n<blockquote>\n<p><a href=\"https:\/\/aistudio.baidu.com\/datasetdetail\/35503\">https:\/\/aistudio.baidu.com\/datasetdetail\/35503<\/a><\/p>\n<\/blockquote>\n<p>\u4e0b\u8f7d\u540e\u53ef\u4ee5\u770b\u5230\u8bad\u7ec3\u96c6\u4e0b\u6709 <code>10<\/code>\u4e2a\u5206\u7c7b\uff1a<\/p>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140804868.png\" alt=\"\u56fe\u7247\" \/>\u5206\u522b\u8868\u793a\uff1a<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">\u5206\u7c7b<\/th>\n<th style=\"text-align: left;\">\u89e3\u91ca<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">c0<\/td>\n<td style=\"text-align: left;\">\u5b89\u5168\u9a7e\u9a76<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c1<\/td>\n<td style=\"text-align: left;\">\u53f3\u624b\u4f7f\u7528\u624b\u673a<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c2<\/td>\n<td style=\"text-align: left;\">\u53f3\u624b\u6253\u7535\u8bdd<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c3<\/td>\n<td style=\"text-align: left;\">\u5de6\u624b\u4f7f\u7528\u624b\u673a<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c4<\/td>\n<td style=\"text-align: left;\">\u5de6\u624b\u6253\u7535\u8bdd<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c5<\/td>\n<td style=\"text-align: left;\">\u64cd\u4f5c\u4e2d\u63a7\u53f0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c6<\/td>\n<td style=\"text-align: left;\">\u559d\u6c34<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c7<\/td>\n<td style=\"text-align: left;\">\u5411\u540e\u4f38\u624b<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c8<\/td>\n<td style=\"text-align: left;\">\u624b\u6478\u5934\u53d1\u6216\u5316\u5986<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">c9<\/td>\n<td style=\"text-align: left;\">\u4e0e\u4eba\u4ea4\u8c08<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u6bcf\u4e2a\u7c7b\u522b\u4e0b\u7684\u793a\u4f8b\u56fe\u50cf\u5982\u4e0b\uff1a<\/p>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140805149.png\" alt=\"\u56fe\u7247\" \/><\/p>\n<p>\u6570\u636e\u96c6\u7684\u5206\u5e03\u5982\u4e0b\uff0c\u6bcf\u4e2a\u7c7b\u522b\u6574\u4f53\u5206\u5e03 <code>2000<\/code> \u5de6\u53f3\uff1a<\/p>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140805274.png\" alt=\"\u56fe\u7247\" \/><\/p>\n<h2>\u4e09\u3001VIT FineTune \u8bad\u7ec3<\/h2>\n<p>\u5728 <code>Pytorch<\/code> \u4e2d\u5df2\u7ecf\u96c6\u6210\u597d\u4e86 <code>VIT<\/code> \u7ed3\u6784\uff0c\u8fd9\u91cc\u4f7f\u7528 <code>vit_b_16<\/code> \u4e3a\u4f8b\uff0c\u53ef\u4ee5\u9009\u62e9\u51bb\u7ed3\u6240\u6709\u539f\u6765\u6a21\u578b\u7684\u53c2\u6570\uff0c\u8ffd\u52a0\u4e24\u5c42\u5168\u94fe\u63a5\u5c42\uff1a<\/p>\n<p>net.py<\/p>\n<pre><code>import torch.nn as nn\nfrom torchvision import models\n\nclass Model(nn.Module):\n    def __init__(self, num_classes):\n        super(Model, self).__init__()\n\n        # \u52a0\u8f7d\u9884\u8bad\u7ec3\u7684 vit_b_16 \u6a21\u578b\n        self.base_model = models.vit_b_16(pretrained=True)\n        print(self.base_model)\n\n        # \u51bb\u7ed3\u4e3b\u5e72\u7f51\u7edc\u7684\u6743\u91cd\n        for param in self.base_model.parameters():\n            param.requires_grad = False\n\n        # \u81ea\u5b9a\u4e49\u5206\u7c7b\u5934\n        self.relu = nn.ReLU()\n        self.fc1 = nn.Linear(self.base_model.heads.head.out_features, 1024)\n        self.dropout1 = nn.Dropout(p=0.2)\n        self.fc2 = nn.Linear(1024, 512)\n        self.dropout2 = nn.Dropout(p=0.1)\n        self.fc3 = nn.Linear(512, num_classes)\n\n    def forward(self, x):\n        x = self.base_model(x)\n        x = self.fc1(x)\n        x = self.relu(x)\n        x = self.dropout1(x)\n        x = self.fc2(x)\n        x = self.relu(x)\n        x = self.dropout2(x)\n        x = self.fc3(x)\n        return x<\/code><\/pre>\n<p>\u6216\u8005\u4e0d\u51bb\u7ed3\u539f\u6709\u7684\u53c2\u6570\uff0c\u4e5f\u4e0d\u6539\u53d8\u539f\u6765\u6a21\u578b\u7684\u7ed3\u6784\uff0c\u5728\u6b64\u57fa\u7840\u4e0a\u7ee7\u7eed\u8bad\u7ec3\u65b0\u7684\u7c7b\u522b\uff0c\u53ef\u4ee5\u4f7f\u7528\u5982\u4e0b\u7ed3\u6784\uff0c\u76f4\u63a5\u5c06 <code>head<\/code> \u5c42\u7684\u8f93\u51fa\u6539\u4e3a\u5206\u7c7b\u7684\u5927\u5c0f\uff1a<\/p>\n<p>net.py<\/p>\n<pre><code>import torch.nn as nn\nfrom torchvision import models\n\nclass Model(nn.Module):\n    def __init__(self, num_classes):\n        super(Model, self).__init__()\n\n        # \u52a0\u8f7d\u9884\u8bad\u7ec3\u6a21\u578b\n        self.base_model = models.vit_b_16(pretrained=True)\n        print(self.base_model)\n\n        # \u83b7\u53d6\u8f93\u5165\u7279\u5f81\u7ef4\u5ea6\n        num_ftrs = self.base_model.heads.head.in_features\n\n        # \u4fee\u6539\u6700\u540e\u4e00\u5c42\u7684\u8f93\u51fa\u6570\n        self.base_model.heads.head = nn.Linear(num_ftrs, num_classes)\n        print(self.base_model)\n\n    def forward(self, x):\n        return self.base_model(x)<\/code><\/pre>\n<p>\u8fd9\u91cc\u6211\u4f7f\u7528\u7b2c\u4e00\u79cd\u65b9\u5f0f\uff0c\u663e\u5b58\u5360\u7528\u6bd4\u8f83\u5c0f\uff0c\u6574\u4f53\u8bad\u7ec3\u8fc7\u7a0b\u5982\u4e0b\uff0c\u5176\u4e2d\u4f7f\u7528 80% \u7684\u6570\u636e\u8bad\u7ec3\uff0c20% \u7684\u6570\u636e\u9a8c\u8bc1\uff1a<\/p>\n<pre><code>import os\nimport json\nimport sys\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, random_split\nfrom torch.utils.tensorboard import SummaryWriter\nfrom torchvision import datasets, models, transforms\nfrom tqdm import tqdm\nfrom net import Model\n\n# \u8bbe\u7f6e\u968f\u673a\u79cd\u5b50\uff0c\u8ba9\u7ed3\u679c\u53ef\u590d\u73b0\ntorch.manual_seed(0)\n\ndef load_data(data_dir, train_ratio, data_transforms, batch_size):\n    &quot;&quot;&quot;\u52a0\u8f7d\u6570\u636e\u96c6\u5e76\u5206\u5272\u4e3a\u8bad\u7ec3\u96c6\u548c\u9a8c\u8bc1\u96c6&quot;&quot;&quot;\n    # \u8bfb\u53d6\u6570\u636e\u96c6\n    dataset = datasets.ImageFolder(data_dir, data_transforms)\n\n    # \u62c6\u5206\u4e3a\u8bad\u7ec3\u96c6\u548c\u9a8c\u8bc1\u96c6\n    train_size = int(train_ratio * len(dataset))\n    val_size = len(dataset) - train_size\n    train_dataset, val_dataset = random_split(dataset, [train_size, val_size])\n\n    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n\n    return train_loader, val_loader, dataset.classes\n\ndef validate_model(model, val_loader, device, criterion):\n    &quot;&quot;&quot;\u9a8c\u8bc1\u6a21\u578b\u6027\u80fd&quot;&quot;&quot;\n    model.eval()\n    correct = 0\n    total = 0\n    running_loss = 0.0\n\n    with torch.no_grad():\n        for inputs, labels in tqdm(val_loader, file=sys.stdout, desc=&quot;Validation&quot;):\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            running_loss += loss.item()\n\n            _, predicted = torch.max(outputs, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n    accuracy = 100 * correct \/ total\n    avg_loss = running_loss \/ len(val_loader)\n\n    return accuracy, avg_loss\n\ndef train_model(model, criterion, optimizer, train_loader, val_loader, device, output_dir, writer, num_epochs=10):\n    &quot;&quot;&quot;\u8bad\u7ec3\u6a21\u578b&quot;&quot;&quot;\n    best_accuracy = 0.0\n    global_step = 0\n\n    for epoch in range(num_epochs):\n        # \u8bad\u7ec3\u9636\u6bb5\n        model.train()\n        running_loss = 0.0\n\n        for inputs, labels in tqdm(train_loader, file=sys.stdout, desc=f&quot;Train Epoch {epoch+1}\/{num_epochs}&quot;):\n            inputs, labels = inputs.to(device), labels.to(device)\n\n            optimizer.zero_grad()\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n\n            running_loss += loss.item()\n            writer.add_scalar(&#039;Loss\/train&#039;, loss.item(), global_step)\n            global_step += 1\n\n        train_loss = running_loss \/ len(train_loader)\n\n        # \u9a8c\u8bc1\u9636\u6bb5\n        accuracy, val_loss = validate_model(model, val_loader, device, criterion)\n\n        # \u8bb0\u5f55\u65e5\u5fd7\n        tqdm.write(f&#039;Epoch {epoch+1}\/{num_epochs}, Device: {device}&#039;)\n        tqdm.write(f&#039;Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, Accuracy: {accuracy:.2f}%&#039;)\n\n        writer.add_scalar(&#039;Loss\/val&#039;, val_loss, epoch)\n        writer.add_scalar(&#039;Accuracy\/val&#039;, accuracy, epoch)\n\n        # \u4fdd\u5b58\u6700\u4f73\u6a21\u578b\n        if accuracy &gt; best_accuracy:\n            torch.save(model.state_dict(), os.path.join(output_dir, &#039;best_model.pth&#039;))\n            best_accuracy = accuracy\n            tqdm.write(f&#039;New best model saved with accuracy: {accuracy:.2f}%&#039;)\n\n    # \u4fdd\u5b58\u6700\u7ec8\u6a21\u578b\n    torch.save(model.state_dict(), os.path.join(output_dir, &#039;last_model.pth&#039;))\n    tqdm.write(f&#039;Training completed. Best accuracy: {best_accuracy:.2f}%&#039;)\n\ndef main():\n    &quot;&quot;&quot;\u4e3b\u51fd\u6570&quot;&quot;&quot;\n    # \u914d\u7f6e\u53c2\u6570\n    data_dir = &#039;imgs\/train&#039;\n    output_dir = &quot;model&quot;\n    logs_dir = &quot;logs&quot;\n    train_ratio = 0.8\n    batch_size = 45\n    learning_rate = 1e-3\n    num_epochs = 50\n\n    # \u6570\u636e\u9884\u5904\u7406\n    data_transforms = transforms.Compose([\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    ])\n\n    # \u521b\u5efa\u8f93\u51fa\u76ee\u5f55\n    if not os.path.exists(output_dir):\n        os.makedirs(output_dir)\n\n    # \u52a0\u8f7d\u6570\u636e\u96c6\n    train_loader, val_loader, classes = load_data(\n        data_dir=data_dir,\n        train_ratio=train_ratio,\n        data_transforms=data_transforms,\n        batch_size=batch_size\n    )\n\n    # \u4fdd\u5b58\u7c7b\u522b\u4fe1\u606f\n    with open(os.path.join(output_dir, &quot;classify.txt&quot;), &quot;w&quot;, encoding=&quot;utf-8&quot;) as f:\n        json.dump(classes, f, ensure_ascii=False, indent=2)\n\n    print(f&quot;Dataset loaded: {len(classes)} classes&quot;)\n    print(f&quot;Classes: {classes}&quot;)\n\n    # \u8bbe\u7f6e\u8bbe\u5907\n    device = torch.device(&quot;cuda:0&quot; if torch.cuda.is_available() else &quot;cpu&quot;)\n    print(f&quot;Using device: {device}&quot;)\n\n    # \u521d\u59cb\u5316\u6a21\u578b\n    model = Model(len(classes))\n    print(&quot;Model architecture:&quot;)\n    print(model)\n\n    # \u635f\u5931\u51fd\u6570\u548c\u4f18\u5316\u5668\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.AdamW(model.parameters(), lr=learning_rate)\n\n    # TensorBoard\u8bb0\u5f55\u5668\n    writer = SummaryWriter(logs_dir)\n\n    # \u8bad\u7ec3\u6a21\u578b\n    model.to(device)\n    train_model(\n        model=model,\n        criterion=criterion,\n        optimizer=optimizer,\n        train_loader=train_loader,\n        val_loader=val_loader,\n        device=device,\n        output_dir=output_dir,\n        writer=writer,\n        num_epochs=num_epochs\n    )\n\n    writer.close()\n    print(&quot;Training completed!&quot;)\n\nif __name__ == &#039;__main__&#039;:\n    main()<\/code><\/pre>\n<p>\u8bad\u7ec3\u671f\u95f4\u5927\u6982\u5360\u7528\u663e\u5b58\u4e24\u4e2a <code>G<\/code>\u5de6\u53f3\uff1a<\/p>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140805371.png\" alt=\"\u56fe\u7247\" \/><\/p>\n<p>\u8bad\u7ec3\u8fc7\u7a0b\uff0c\u53ef\u4ee5\u770b\u5230\u9a8c\u8bc1\u96c6\u7684\u51c6\u786e\u7387\u5728\u9010\u6b65\u63d0\u5347\u4ee5\u53ca <code>loss<\/code>\u5728\u9010\u6b65\u6536\u655b\uff1a<\/p>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140805759.png\" alt=\"\u56fe\u7247\" \/><\/p>\n<p>\u8bad\u7ec3\u7ed3\u675f\u540e\uff0c\u53ef\u4ee5\u67e5\u770b\u4e0b <code>tensorboard<\/code> \u4e2d\u4f60\u7684 <code>loss<\/code> \u548c \u51c6\u786e\u7387\u7684\u66f2\u7ebf\uff1a<\/p>\n<pre><code>tensorboard --logdir=logs --bind_all<\/code><\/pre>\n<p>\u5728 \u6d4f\u89c8\u5668\u8bbf\u95ee <code>http:ip:6006\/<\/code><\/p>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140805966.png\" alt=\"\u56fe\u7247\" \/><\/p>\n<p>\u5728\u9a8c\u8bc1\u96c6\u4e0a\u7684\u51c6\u786e\u7387\u8fbe\u5230 <code>98.5<\/code> \u5de6\u53f3\uff0c <code>loss<\/code> \u7684\u6ce2\u52a8\u8fd8\u662f\u86ee\u5927\u7684\uff0c\u5927\u5bb6\u4e5f\u53ef\u4ee5\u52a0\u5165\u66f4\u591a\u4f18\u5316\u7b56\u7565\u8fdb\u6765\u3002<\/p>\n<h2>\u56db\u3001\u6a21\u578b\u6d4b\u8bd5<\/h2>\n<pre><code>import os\nimport json\nimport torch\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nfrom torchvision import transforms\nfrom net import Model\n\n# \u8bbe\u7f6e\u4e2d\u6587\u5b57\u4f53\nplt.rcParams[&#039;font.sans-serif&#039;] = [&#039;SimHei&#039;]\nplt.rcParams[&#039;axes.unicode_minus&#039;] = False  # \u89e3\u51b3\u8d1f\u53f7\u663e\u793a\u95ee\u9898\n\n# \u5206\u7c7b\u6807\u7b7e\u4e2d\u6587\u6620\u5c04\nclassify_cn = {\n    &quot;c0&quot;: &quot;\u5b89\u5168\u9a7e\u9a76&quot;,\n    &quot;c1&quot;: &quot;\u53f3\u624b\u4f7f\u7528\u624b\u673a&quot;, \n    &quot;c2&quot;: &quot;\u53f3\u624b\u6253\u7535\u8bdd&quot;,\n    &quot;c3&quot;: &quot;\u5de6\u624b\u4f7f\u7528\u624b\u673a&quot;,\n    &quot;c4&quot;: &quot;\u5de6\u624b\u6253\u7535\u8bdd&quot;,\n    &quot;c5&quot;: &quot;\u64cd\u4f5c\u4e2d\u63a7\u53f0&quot;,\n    &quot;c6&quot;: &quot;\u559d\u6c34&quot;,\n    &quot;c7&quot;: &quot;\u5411\u540e\u4f38\u624b&quot;,\n    &quot;c8&quot;: &quot;\u624b\u6478\u5934\u53d1\u6216\u5316\u5986&quot;,\n    &quot;c9&quot;: &quot;\u4e0e\u4eba\u4ea4\u8c08&quot;\n}\n\ndef predict_image(model, image_path, data_transforms, device, classify, classify_cn):\n    &quot;&quot;&quot;\u9884\u6d4b\u5355\u5f20\u56fe\u50cf\u7684\u7c7b\u522b&quot;&quot;&quot;\n    # \u52a0\u8f7d\u548c\u9884\u5904\u7406\u56fe\u50cf\n    image = Image.open(image_path).convert(&#039;RGB&#039;)\n    input_tensor = data_transforms(image).unsqueeze(0).to(device)\n\n    # \u6a21\u578b\u9884\u6d4b\n    model.eval()\n    with torch.no_grad():\n        output = model(input_tensor)\n        _, predicted = torch.max(output.data, 1)\n        predicted_idx = predicted[0].item()\n\n    # \u83b7\u53d6\u9884\u6d4b\u6807\u7b7e\n    class_id = classify[predicted_idx]\n    label = classify_cn.get(class_id, f&quot;\u672a\u77e5\u7c7b\u522b({class_id})&quot;)\n    confidence = torch.softmax(output, 1)[0][predicted_idx].item()\n\n    return image, label, confidence\n\ndef main():\n    # \u914d\u7f6e\u53c2\u6570\n    image_dir = &quot;imgs\/test&quot;\n    model_path = &quot;model\/best_model.pth&quot;\n    classify_file = &quot;model\/classify.txt&quot;\n\n    # \u8bfb\u53d6\u5206\u7c7b\u6807\u7b7e\n    try:\n        with open(classify_file, &quot;r&quot;, encoding=&quot;utf-8&quot;) as f:\n            classify = json.load(f)\n        print(f&quot;\u52a0\u8f7d\u5206\u7c7b\u6807\u7b7e\u6210\u529f\uff0c\u5171{len(classify)}\u4e2a\u7c7b\u522b&quot;)\n    except FileNotFoundError:\n        print(f&quot;\u9519\u8bef\uff1a\u627e\u4e0d\u5230\u5206\u7c7b\u6587\u4ef6 {classify_file}&quot;)\n        return\n    except json.JSONDecodeError:\n        print(&quot;\u9519\u8bef\uff1a\u5206\u7c7b\u6587\u4ef6\u683c\u5f0f\u4e0d\u6b63\u786e&quot;)\n        return\n\n    # \u8bbe\u7f6e\u8bbe\u5907\n    device = torch.device(&quot;cuda:0&quot; if torch.cuda.is_available() else &quot;cpu&quot;)\n    print(f&quot;\u4f7f\u7528\u8bbe\u5907: {device}&quot;)\n\n    # \u52a0\u8f7d\u6a21\u578b\n    try:\n        model = Model(len(classify))\n        model.load_state_dict(torch.load(model_path, map_location=device))\n        model = model.to(device)\n        print(&quot;\u6a21\u578b\u52a0\u8f7d\u6210\u529f&quot;)\n    except FileNotFoundError:\n        print(f&quot;\u9519\u8bef\uff1a\u627e\u4e0d\u5230\u6a21\u578b\u6587\u4ef6 {model_path}&quot;)\n        return\n    except Exception as e:\n        print(f&quot;\u6a21\u578b\u52a0\u8f7d\u5931\u8d25: {e}&quot;)\n        return\n\n    # \u6570\u636e\u9884\u5904\u7406\n    data_transforms = transforms.Compose([\n        transforms.Resize(224),\n        transforms.CenterCrop(224),\n        transforms.ToTensor(),\n        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n    ])\n\n    # \u83b7\u53d6\u6d4b\u8bd5\u56fe\u50cf\n    try:\n        image_files = [f for f in os.listdir(image_dir) \n                      if f.lower().endswith((&#039;.png&#039;, &#039;.jpg&#039;, &#039;.jpeg&#039;))]\n        if not image_files:\n            print(f&quot;\u5728\u76ee\u5f55 {image_dir} \u4e2d\u672a\u627e\u5230\u56fe\u50cf\u6587\u4ef6&quot;)\n            return\n        print(f&quot;\u627e\u5230 {len(image_files)} \u5f20\u6d4b\u8bd5\u56fe\u50cf&quot;)\n    except FileNotFoundError:\n        print(f&quot;\u9519\u8bef\uff1a\u627e\u4e0d\u5230\u6d4b\u8bd5\u76ee\u5f55 {image_dir}&quot;)\n        return\n\n    # \u5c06\u56fe\u50cf\u5206\u62104\u4e2a\u4e00\u7ec4\u8fdb\u884c\u663e\u793a\n    image_groups = [image_files[i:i+4] for i in range(0, len(image_files), 4)]\n\n    for group_idx, image_names in enumerate(image_groups):\n        # \u521b\u5efa\u5b50\u56fe\n        fig, axes = plt.subplots(2, 2, figsize=(12, 10))\n        fig.suptitle(f&#039;\u56fe\u50cf\u5206\u7c7b\u9884\u6d4b\u7ed3\u679c (\u7b2c{group_idx + 1}\u7ec4)&#039;, fontsize=16, fontweight=&#039;bold&#039;)\n\n        for i, image_name in enumerate(image_names):\n            row, col = i \/\/ 2, i % 2\n            ax = axes[row, col]\n\n            try:\n                image_path = os.path.join(image_dir, image_name)\n                image, label, confidence = predict_image(\n                    model, image_path, data_transforms, device, classify, classify_cn\n                )\n\n                # \u663e\u793a\u56fe\u50cf\u548c\u9884\u6d4b\u7ed3\u679c\n                ax.imshow(image)\n                ax.set_title(f&#039;{label}\\n\u7f6e\u4fe1\u5ea6: {confidence:.2%}&#039;, fontsize=12)\n                ax.axis(&#039;off&#039;)\n\n                # \u5728\u56fe\u50cf\u6587\u4ef6\u540d\u4e0b\u65b9\u663e\u793a\u6587\u4ef6\u540d\n                ax.text(0.5, -0.1, image_name, transform=ax.transAxes, \n                       ha=&#039;center&#039;, fontsize=9, style=&#039;italic&#039;)\n\n            except Exception as e:\n                print(f&quot;\u5904\u7406\u56fe\u50cf {image_name} \u65f6\u51fa\u9519: {e}&quot;)\n                ax.text(0.5, 0.5, f&quot;\u52a0\u8f7d\u5931\u8d25\\n{image_name}&quot;, \n                       ha=&#039;center&#039;, va=&#039;center&#039;, transform=ax.transAxes)\n                ax.axis(&#039;off&#039;)\n\n        # \u9690\u85cf\u591a\u4f59\u7684\u5b50\u56fe\n        for i in range(len(image_names), 4):\n            row, col = i \/\/ 2, i % 2\n            axes[row, col].axis(&#039;off&#039;)\n\n        plt.tight_layout()\n        plt.subplots_adjust(top=0.92)\n        plt.show()\n\n        # \u8be2\u95ee\u662f\u5426\u7ee7\u7eed\u663e\u793a\u4e0b\u4e00\u7ec4\n        if group_idx &lt; len(image_groups) - 1:\n            continue_input = input(&quot;\u6309Enter\u7ee7\u7eed\u663e\u793a\u4e0b\u4e00\u7ec4\uff0c\u8f93\u5165q\u9000\u51fa: &quot;)\n            if continue_input.lower() == &#039;q&#039;:\n                break\n\nif __name__ == &#039;__main__&#039;:\n    main()<\/code><\/pre>\n<p><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140806320.png\" alt=\"\u56fe\u7247\" \/><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140806652.png\" alt=\"\u56fe\u7247\" \/><img src=\"https:\/\/lemon-guess.oss-cn-hangzhou.aliyuncs.com\/img\/640-20251107140806869.png\" alt=\"\u56fe\u7247\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u4e00\u3001VisionTransformer(VIT) \u4ecb\u7ecd \u5927\u6a21\u578b\u5df2\u7ecf\u6210\u4e3a\u4eba\u5de5\u667a\u80fd\u9886\u57df\u7684\u70ed\u95e8\u8bdd\u9898\u3002\u5728\u8fd9\u80a1\u70ed\u6f6e\u4e2d\uff0c\u5927\u6a21\u578b\u7684\u6838\u5fc3\u7ed3\u6784 Trans   \u2026 &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0},"categories":[289],"tags":[],"_links":{"self":[{"href":"\/index.php?rest_route=\/wp\/v2\/posts\/9474"}],"collection":[{"href":"\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9474"}],"version-history":[{"count":1,"href":"\/index.php?rest_route=\/wp\/v2\/posts\/9474\/revisions"}],"predecessor-version":[{"id":9475,"href":"\/index.php?rest_route=\/wp\/v2\/posts\/9474\/revisions\/9475"}],"wp:attachment":[{"href":"\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9474"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9474"},{"taxonomy":"post_tag","embeddable":true,"href":"\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9474"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}