From 40e7e4d91b9bc395d95ba5132022c0f48e75f691 Mon Sep 17 00:00:00 2001 From: ChenYuyang Date: Sun, 19 Apr 2020 16:30:35 +0800 Subject: [PATCH 1/6] =?UTF-8?q?model.forward().shape(0)=20=E5=BA=94?= =?UTF-8?q?=E8=AF=A5=E6=98=AFbatch=5Fsize=EF=BC=8C=E4=B8=8D=E7=84=B6?= =?UTF-8?q?=E5=A4=9A=E6=98=BE=E5=8D=A1=E8=AE=AD=E7=BB=83=E5=BE=88=E9=9A=BE?= =?UTF-8?q?=E5=AE=8C=E6=88=90=20.Make=20sure=20model.forward().shape(0)=20?= =?UTF-8?q?is=20batch=5Fsize=20otherwise=20it's=20difficult=20to=20change?= =?UTF-8?q?=20code=20for=20multipgpu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ctc_pytorch-checkpoint.ipynb | 878 ++++++++++++++++++ ctc_pytorch.ipynb | 19 +- 2 files changed, 888 insertions(+), 9 deletions(-) create mode 100644 .ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb diff --git a/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb b/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb new file mode 100644 index 0000000..70fa945 --- /dev/null +++ b/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb @@ -0,0 +1,878 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 导入必要的库\n", + "\n", + "我们需要导入一个叫 [captcha](https://github.com/lepture/captcha/) 的库来生成验证码。\n", + "\n", + "我们生成验证码的字符由数字和大写字母组成。\n", + "\n", + "```sh\n", + "pip install captcha numpy matplotlib torch torchvision tqdm\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:45.698786Z", + "start_time": "2019-06-18T11:19:45.381128Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 192 64 4 37\n" + ] + } + ], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "from torch.utils.data import Dataset, DataLoader\n", + "from torchvision.transforms.functional import to_tensor, to_pil_image\n", + "\n", + "from captcha.image import ImageCaptcha\n", + "from tqdm import tqdm\n", + "import random\n", + "import numpy as np\n", + "from collections import OrderedDict\n", + "\n", + "import string\n", + "characters = '-' + string.digits + string.ascii_uppercase\n", + "width, height, n_len, n_classes = 192, 64, 4, len(characters)\n", + "n_input_length = 12\n", + "print(characters, width, height, n_len, n_classes)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 搭建数据集" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:45.704071Z", + "start_time": "2019-06-18T11:19:45.700019Z" + } + }, + "outputs": [], + "source": [ + "class CaptchaDataset(Dataset):\n", + " def __init__(self, characters, length, width, height, input_length, label_length):\n", + " super(CaptchaDataset, self).__init__()\n", + " self.characters = characters\n", + " self.length = length\n", + " self.width = width\n", + " self.height = height\n", + " self.input_length = input_length\n", + " self.label_length = label_length\n", + " self.n_class = len(characters)\n", + " self.generator = ImageCaptcha(width=width, height=height)\n", + "\n", + " def __len__(self):\n", + " return self.length\n", + " \n", + " def __getitem__(self, index):\n", + " random_str = ''.join([random.choice(self.characters[1:]) for j in range(self.label_length)])\n", + " image = to_tensor(self.generator.generate_image(random_str))\n", + " target = torch.tensor([self.characters.find(x) for x in random_str], dtype=torch.long)\n", + " input_length = torch.full(size=(1, ), fill_value=self.input_length, dtype=torch.long)\n", + " target_length = torch.full(size=(1, ), fill_value=self.label_length, dtype=torch.long)\n", + " return image, target, input_length, target_length" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 测试数据集" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:45.733929Z", + "start_time": "2019-06-18T11:19:45.705130Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "67NQ tensor([12]) tensor([4])\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset = CaptchaDataset(characters, 1, width, height, n_input_length, n_len)\n", + "image, target, input_length, label_length = dataset[0]\n", + "print(''.join([characters[x] for x in target]), input_length, label_length)\n", + "to_pil_image(image)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 初始化数据集生成器" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:45.737300Z", + "start_time": "2019-06-18T11:19:45.735033Z" + } + }, + "outputs": [], + "source": [ + "batch_size = 128\n", + "train_set = CaptchaDataset(characters, 1000 * batch_size, width, height, n_input_length, n_len)\n", + "valid_set = CaptchaDataset(characters, 100 * batch_size, width, height, n_input_length, n_len)\n", + "train_loader = DataLoader(train_set, batch_size=batch_size, num_workers=12)\n", + "valid_loader = DataLoader(valid_set, batch_size=batch_size, num_workers=12)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 搭建模型" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:45.744324Z", + "start_time": "2019-06-18T11:19:45.738366Z" + } + }, + "outputs": [], + "source": [ + "class Model(nn.Module):\n", + " def __init__(self, n_classes, input_shape=(3, 64, 128)):\n", + " super(Model, self).__init__()\n", + " self.input_shape = input_shape\n", + " channels = [32, 64, 128, 256, 256]\n", + " layers = [2, 2, 2, 2, 2]\n", + " kernels = [3, 3, 3, 3, 3]\n", + " pools = [2, 2, 2, 2, (2, 1)]\n", + " modules = OrderedDict()\n", + " \n", + " def cba(name, in_channels, out_channels, kernel_size):\n", + " modules[f'conv{name}'] = nn.Conv2d(in_channels, out_channels, kernel_size,\n", + " padding=(1, 1) if kernel_size == 3 else 0)\n", + " modules[f'bn{name}'] = nn.BatchNorm2d(out_channels)\n", + " modules[f'relu{name}'] = nn.ReLU(inplace=True)\n", + " \n", + " last_channel = 3\n", + " for block, (n_channel, n_layer, n_kernel, k_pool) in enumerate(zip(channels, layers, kernels, pools)):\n", + " for layer in range(1, n_layer + 1):\n", + " cba(f'{block+1}{layer}', last_channel, n_channel, n_kernel)\n", + " last_channel = n_channel\n", + " modules[f'pool{block + 1}'] = nn.MaxPool2d(k_pool)\n", + " modules[f'dropout'] = nn.Dropout(0.25, inplace=True)\n", + " \n", + " self.cnn = nn.Sequential(modules)\n", + " self.lstm = nn.LSTM(input_size=self.infer_features(), hidden_size=128, num_layers=2, bidirectional=True)\n", + " self.fc = nn.Linear(in_features=256, out_features=n_classes)\n", + " \n", + " def infer_features(self):\n", + " x = torch.zeros((1,)+self.input_shape)\n", + " x = self.cnn(x)\n", + " x = x.reshape(x.shape[0], -1, x.shape[-1])\n", + " return x.shape[1]\n", + "\n", + " def forward(self, x):\n", + " x = self.cnn(x)\n", + " x = x.reshape(x.shape[0], -1, x.shape[-1])\n", + " x = x.permute(2, 0, 1)\n", + " x, _ = self.lstm(x)\n", + " x = self.fc(x)\n", + " return x.permute(0,1,2) # setting batch-size to first dim makes it easy to change code of multi-gpu training " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 测试模型输出尺寸" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:46.033594Z", + "start_time": "2019-06-18T11:19:45.745300Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "torch.Size([12, 32, 37])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model = Model(n_classes, input_shape=(3, height, width))\n", + "inputs = torch.zeros((32, 3, height, width))\n", + "outputs = model(inputs)\n", + "outputs.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 初始化模型" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:48.035272Z", + "start_time": "2019-06-18T11:19:46.034771Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Model(\n", + " (cnn): Sequential(\n", + " (conv11): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn11): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu11): ReLU(inplace)\n", + " (conv12): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn12): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu12): ReLU(inplace)\n", + " (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " (conv21): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn21): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu21): ReLU(inplace)\n", + " (conv22): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn22): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu22): ReLU(inplace)\n", + " (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " (conv31): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn31): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu31): ReLU(inplace)\n", + " (conv32): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn32): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu32): ReLU(inplace)\n", + " (pool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " (conv41): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn41): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu41): ReLU(inplace)\n", + " (conv42): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn42): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu42): ReLU(inplace)\n", + " (pool4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " (conv51): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn51): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu51): ReLU(inplace)\n", + " (conv52): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (bn52): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu52): ReLU(inplace)\n", + " (pool5): MaxPool2d(kernel_size=(2, 1), stride=(2, 1), padding=0, dilation=1, ceil_mode=False)\n", + " (dropout): Dropout(p=0.25, inplace)\n", + " )\n", + " (lstm): LSTM(512, 128, num_layers=2, bidirectional=True)\n", + " (fc): Linear(in_features=256, out_features=37, bias=True)\n", + ")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model = Model(n_classes, input_shape=(3, height, width))\n", + "model = model.cuda()\n", + "model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 解码函数和准确率计算函数" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:48.040043Z", + "start_time": "2019-06-18T11:19:48.036404Z" + } + }, + "outputs": [], + "source": [ + "def decode(sequence):\n", + " a = ''.join([characters[x] for x in sequence])\n", + " s = ''.join([x for j, x in enumerate(a[:-1]) if x != characters[0] and x != a[j+1]])\n", + " if len(s) == 0:\n", + " return ''\n", + " if a[-1] != characters[0] and s[-1] != a[-1]:\n", + " s += a[-1]\n", + " return s\n", + "\n", + "def decode_target(sequence):\n", + " return ''.join([characters[x] for x in sequence]).replace(' ', '')\n", + "\n", + "def calc_acc(target, output):\n", + " output_argmax = output.detach().permute(1, 0, 2).argmax(dim=-1)\n", + " target = target.cpu().numpy()\n", + " output_argmax = output_argmax.cpu().numpy()\n", + " a = np.array([decode_target(true) == decode(pred) for true, pred in zip(target, output_argmax)])\n", + " return a.mean()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 训练模型" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T11:19:48.052899Z", + "start_time": "2019-06-18T11:19:48.041088Z" + } + }, + "outputs": [], + "source": [ + "def train(model, optimizer, epoch, dataloader):\n", + " model.train()\n", + " loss_mean = 0\n", + " acc_mean = 0\n", + " with tqdm(dataloader) as pbar:\n", + " for batch_index, (data, target, input_lengths, target_lengths) in enumerate(pbar):\n", + " data, target = data.cuda(), target.cuda()\n", + " \n", + " optimizer.zero_grad()\n", + " output = model(data)\n", + " \n", + " output_log_softmax = F.log_softmax(output, dim=-1)\n", + " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", + " \n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " loss = loss.item()\n", + " acc = calc_acc(target, output)\n", + " \n", + " if batch_index == 0:\n", + " loss_mean = loss\n", + " acc_mean = acc\n", + " \n", + " loss_mean = 0.1 * loss + 0.9 * loss_mean\n", + " acc_mean = 0.1 * acc + 0.9 * acc_mean\n", + " \n", + " pbar.set_description(f'Epoch: {epoch} Loss: {loss_mean:.4f} Acc: {acc_mean:.4f} ')\n", + "\n", + "def valid(model, optimizer, epoch, dataloader):\n", + " model.eval()\n", + " with tqdm(dataloader) as pbar, torch.no_grad():\n", + " loss_sum = 0\n", + " acc_sum = 0\n", + " for batch_index, (data, target, input_lengths, target_lengths) in enumerate(pbar):\n", + " data, target = data.cuda(), target.cuda()\n", + " \n", + " output = model(data)\n", + " output_log_softmax = F.log_softmax(output, dim=-1)\n", + " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", + " \n", + " loss = loss.item()\n", + " acc = calc_acc(target, output)\n", + " \n", + " loss_sum += loss\n", + " acc_sum += acc\n", + " \n", + " loss_mean = loss_sum / (batch_index + 1)\n", + " acc_mean = acc_sum / (batch_index + 1)\n", + " \n", + " pbar.set_description(f'Test : {epoch} Loss: {loss_mean:.4f} Acc: {acc_mean:.4f} ')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T12:18:50.675432Z", + "start_time": "2019-06-18T11:19:48.053976Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch: 1 Loss: 3.7244 Acc: 0.0000 : 100%|██████████| 1000/1000 [01:52<00:00, 8.93it/s]\n", + "Test : 1 Loss: 3.7294 Acc: 0.0000 : 100%|██████████| 100/100 [00:05<00:00, 17.52it/s]\n", + "Epoch: 2 Loss: 3.7359 Acc: 0.0000 : 100%|██████████| 1000/1000 [01:52<00:00, 9.01it/s]\n", + "Test : 2 Loss: 3.7290 Acc: 0.0000 : 100%|██████████| 100/100 [00:05<00:00, 17.65it/s]\n", + "Epoch: 3 Loss: 3.7199 Acc: 0.0000 : 100%|██████████| 1000/1000 [01:52<00:00, 9.02it/s]\n", + "Test : 3 Loss: 3.7271 Acc: 0.0000 : 100%|██████████| 100/100 [00:05<00:00, 16.77it/s]\n", + "Epoch: 4 Loss: 2.3948 Acc: 0.0038 : 100%|██████████| 1000/1000 [01:52<00:00, 8.89it/s]\n", + "Test : 4 Loss: 2.8448 Acc: 0.0015 : 100%|██████████| 100/100 [00:05<00:00, 16.95it/s]\n", + "Epoch: 5 Loss: 0.1477 Acc: 0.8431 : 100%|██████████| 1000/1000 [01:52<00:00, 8.92it/s]\n", + "Test : 5 Loss: 0.1622 Acc: 0.8145 : 100%|██████████| 100/100 [00:05<00:00, 17.31it/s]\n", + "Epoch: 6 Loss: 0.0860 Acc: 0.8926 : 100%|██████████| 1000/1000 [01:52<00:00, 8.94it/s]\n", + "Test : 6 Loss: 0.1019 Acc: 0.8745 : 100%|██████████| 100/100 [00:05<00:00, 17.48it/s]\n", + "Epoch: 7 Loss: 0.0414 Acc: 0.9436 : 100%|██████████| 1000/1000 [01:52<00:00, 8.91it/s]\n", + "Test : 7 Loss: 0.1066 Acc: 0.8970 : 100%|██████████| 100/100 [00:05<00:00, 25.14it/s]\n", + "Epoch: 8 Loss: 0.0317 Acc: 0.9527 : 100%|██████████| 1000/1000 [01:52<00:00, 9.19it/s]\n", + "Test : 8 Loss: 0.2585 Acc: 0.8132 : 100%|██████████| 100/100 [00:05<00:00, 17.50it/s]\n", + "Epoch: 9 Loss: 0.0282 Acc: 0.9620 : 100%|██████████| 1000/1000 [01:52<00:00, 8.85it/s]\n", + "Test : 9 Loss: 0.0775 Acc: 0.9416 : 100%|██████████| 100/100 [00:05<00:00, 17.50it/s]\n", + "Epoch: 10 Loss: 0.0235 Acc: 0.9626 : 100%|██████████| 1000/1000 [01:52<00:00, 9.02it/s]\n", + "Test : 10 Loss: 0.0321 Acc: 0.9519 : 100%|██████████| 100/100 [00:05<00:00, 17.20it/s]\n", + "Epoch: 11 Loss: 0.0210 Acc: 0.9742 : 100%|██████████| 1000/1000 [01:52<00:00, 9.00it/s]\n", + "Test : 11 Loss: 0.0268 Acc: 0.9686 : 100%|██████████| 100/100 [00:05<00:00, 17.27it/s]\n", + "Epoch: 12 Loss: 0.0196 Acc: 0.9734 : 100%|██████████| 1000/1000 [01:52<00:00, 8.92it/s]\n", + "Test : 12 Loss: 0.0386 Acc: 0.9555 : 100%|██████████| 100/100 [00:05<00:00, 26.65it/s]\n", + "Epoch: 13 Loss: 0.0207 Acc: 0.9676 : 100%|██████████| 1000/1000 [01:52<00:00, 8.80it/s]\n", + "Test : 13 Loss: 0.0269 Acc: 0.9647 : 100%|██████████| 100/100 [00:05<00:00, 23.93it/s]\n", + "Epoch: 14 Loss: 0.0195 Acc: 0.9734 : 100%|██████████| 1000/1000 [01:52<00:00, 8.98it/s]\n", + "Test : 14 Loss: 0.0163 Acc: 0.9767 : 100%|██████████| 100/100 [00:05<00:00, 17.39it/s]\n", + "Epoch: 15 Loss: 0.0181 Acc: 0.9751 : 100%|██████████| 1000/1000 [01:52<00:00, 9.02it/s]\n", + "Test : 15 Loss: 0.0242 Acc: 0.9669 : 100%|██████████| 100/100 [00:05<00:00, 17.34it/s]\n", + "Epoch: 16 Loss: 0.0126 Acc: 0.9840 : 100%|██████████| 1000/1000 [01:52<00:00, 9.02it/s]\n", + "Test : 16 Loss: 0.0298 Acc: 0.9570 : 100%|██████████| 100/100 [00:05<00:00, 24.91it/s]\n", + "Epoch: 17 Loss: 0.0120 Acc: 0.9833 : 100%|██████████| 1000/1000 [01:52<00:00, 8.85it/s]\n", + "Test : 17 Loss: 0.0185 Acc: 0.9722 : 100%|██████████| 100/100 [00:05<00:00, 17.38it/s]\n", + "Epoch: 18 Loss: 0.0139 Acc: 0.9814 : 100%|██████████| 1000/1000 [01:52<00:00, 8.89it/s]\n", + "Test : 18 Loss: 0.0138 Acc: 0.9809 : 100%|██████████| 100/100 [00:05<00:00, 25.59it/s]\n", + "Epoch: 19 Loss: 0.0138 Acc: 0.9779 : 100%|██████████| 1000/1000 [01:52<00:00, 9.01it/s]\n", + "Test : 19 Loss: 0.3607 Acc: 0.7903 : 100%|██████████| 100/100 [00:05<00:00, 17.49it/s]\n", + "Epoch: 20 Loss: 0.0128 Acc: 0.9799 : 100%|██████████| 1000/1000 [01:52<00:00, 8.87it/s]\n", + "Test : 20 Loss: 0.2395 Acc: 0.8163 : 100%|██████████| 100/100 [00:05<00:00, 25.22it/s]\n", + "Epoch: 21 Loss: 0.0092 Acc: 0.9887 : 100%|██████████| 1000/1000 [01:52<00:00, 8.93it/s]\n", + "Test : 21 Loss: 0.0358 Acc: 0.9598 : 100%|██████████| 100/100 [00:05<00:00, 17.50it/s]\n", + "Epoch: 22 Loss: 0.0116 Acc: 0.9841 : 100%|██████████| 1000/1000 [01:52<00:00, 8.92it/s]\n", + "Test : 22 Loss: 0.4531 Acc: 0.5920 : 100%|██████████| 100/100 [00:05<00:00, 17.23it/s]\n", + "Epoch: 23 Loss: 0.0116 Acc: 0.9846 : 100%|██████████| 1000/1000 [01:52<00:00, 8.96it/s]\n", + "Test : 23 Loss: 0.0089 Acc: 0.9878 : 100%|██████████| 100/100 [00:05<00:00, 17.51it/s]\n", + "Epoch: 24 Loss: 0.0079 Acc: 0.9884 : 100%|██████████| 1000/1000 [01:52<00:00, 8.94it/s]\n", + "Test : 24 Loss: 0.0093 Acc: 0.9871 : 100%|██████████| 100/100 [00:05<00:00, 17.42it/s]\n", + "Epoch: 25 Loss: 0.0078 Acc: 0.9904 : 100%|██████████| 1000/1000 [01:52<00:00, 8.74it/s]\n", + "Test : 25 Loss: 0.0154 Acc: 0.9775 : 100%|██████████| 100/100 [00:05<00:00, 17.39it/s]\n", + "Epoch: 26 Loss: 0.0086 Acc: 0.9896 : 100%|██████████| 1000/1000 [01:52<00:00, 8.98it/s]\n", + "Test : 26 Loss: 0.0803 Acc: 0.9563 : 100%|██████████| 100/100 [00:05<00:00, 17.20it/s]\n", + "Epoch: 27 Loss: 0.0104 Acc: 0.9862 : 100%|██████████| 1000/1000 [01:52<00:00, 8.87it/s]\n", + "Test : 27 Loss: 0.0557 Acc: 0.9373 : 100%|██████████| 100/100 [00:05<00:00, 17.57it/s]\n", + "Epoch: 28 Loss: 0.0077 Acc: 0.9910 : 100%|██████████| 1000/1000 [01:52<00:00, 8.97it/s]\n", + "Test : 28 Loss: 0.0081 Acc: 0.9905 : 100%|██████████| 100/100 [00:05<00:00, 26.49it/s]\n", + "Epoch: 29 Loss: 0.0079 Acc: 0.9912 : 100%|██████████| 1000/1000 [01:52<00:00, 9.20it/s]\n", + "Test : 29 Loss: 0.0717 Acc: 0.9101 : 100%|██████████| 100/100 [00:05<00:00, 17.47it/s]\n", + "Epoch: 30 Loss: 0.0076 Acc: 0.9894 : 100%|██████████| 1000/1000 [01:52<00:00, 9.02it/s]\n", + "Test : 30 Loss: 0.0114 Acc: 0.9846 : 100%|██████████| 100/100 [00:05<00:00, 17.36it/s]\n" + ] + } + ], + "source": [ + "optimizer = torch.optim.Adam(model.parameters(), 1e-3, amsgrad=True)\n", + "epochs = 30\n", + "for epoch in range(1, epochs + 1):\n", + " train(model, optimizer, epoch, train_loader)\n", + " valid(model, optimizer, epoch, valid_loader)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T12:48:21.757260Z", + "start_time": "2019-06-18T12:18:50.676872Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch: 1 Loss: 0.0045 Acc: 0.9938 : 100%|██████████| 1000/1000 [01:52<00:00, 8.96it/s]\n", + "Test : 1 Loss: 0.0044 Acc: 0.9944 : 100%|██████████| 100/100 [00:05<00:00, 17.20it/s]\n", + "Epoch: 2 Loss: 0.0039 Acc: 0.9935 : 100%|██████████| 1000/1000 [01:52<00:00, 8.94it/s]\n", + "Test : 2 Loss: 0.0040 Acc: 0.9943 : 100%|██████████| 100/100 [00:05<00:00, 17.47it/s]\n", + "Epoch: 3 Loss: 0.0047 Acc: 0.9948 : 100%|██████████| 1000/1000 [01:52<00:00, 9.09it/s]\n", + "Test : 3 Loss: 0.0043 Acc: 0.9941 : 100%|██████████| 100/100 [00:05<00:00, 17.12it/s]\n", + "Epoch: 4 Loss: 0.0049 Acc: 0.9935 : 100%|██████████| 1000/1000 [01:52<00:00, 9.08it/s]\n", + "Test : 4 Loss: 0.0050 Acc: 0.9941 : 100%|██████████| 100/100 [00:05<00:00, 17.33it/s]\n", + "Epoch: 5 Loss: 0.0033 Acc: 0.9951 : 100%|██████████| 1000/1000 [01:52<00:00, 9.11it/s]\n", + "Test : 5 Loss: 0.0047 Acc: 0.9937 : 100%|██████████| 100/100 [00:05<00:00, 26.28it/s]\n", + "Epoch: 6 Loss: 0.0029 Acc: 0.9959 : 100%|██████████| 1000/1000 [01:52<00:00, 8.84it/s]\n", + "Test : 6 Loss: 0.0037 Acc: 0.9960 : 100%|██████████| 100/100 [00:05<00:00, 25.36it/s]\n", + "Epoch: 7 Loss: 0.0030 Acc: 0.9969 : 100%|██████████| 1000/1000 [01:52<00:00, 8.91it/s]\n", + "Test : 7 Loss: 0.0039 Acc: 0.9953 : 100%|██████████| 100/100 [00:05<00:00, 26.32it/s]\n", + "Epoch: 8 Loss: 0.0049 Acc: 0.9938 : 100%|██████████| 1000/1000 [01:52<00:00, 8.91it/s]\n", + "Test : 8 Loss: 0.0036 Acc: 0.9949 : 100%|██████████| 100/100 [00:05<00:00, 26.22it/s]\n", + "Epoch: 9 Loss: 0.0026 Acc: 0.9967 : 100%|██████████| 1000/1000 [01:52<00:00, 8.84it/s]\n", + "Test : 9 Loss: 0.0041 Acc: 0.9948 : 100%|██████████| 100/100 [00:05<00:00, 17.29it/s]\n", + "Epoch: 10 Loss: 0.0025 Acc: 0.9975 : 100%|██████████| 1000/1000 [01:52<00:00, 8.86it/s]\n", + "Test : 10 Loss: 0.0026 Acc: 0.9963 : 100%|██████████| 100/100 [00:05<00:00, 17.10it/s]\n", + "Epoch: 11 Loss: 0.0053 Acc: 0.9942 : 100%|██████████| 1000/1000 [01:52<00:00, 8.96it/s]\n", + "Test : 11 Loss: 0.0030 Acc: 0.9959 : 100%|██████████| 100/100 [00:05<00:00, 24.42it/s]\n", + "Epoch: 12 Loss: 0.0021 Acc: 0.9974 : 100%|██████████| 1000/1000 [01:52<00:00, 8.66it/s]\n", + "Test : 12 Loss: 0.0028 Acc: 0.9964 : 100%|██████████| 100/100 [00:05<00:00, 26.51it/s]\n", + "Epoch: 13 Loss: 0.0027 Acc: 0.9960 : 100%|██████████| 1000/1000 [01:52<00:00, 8.95it/s]\n", + "Test : 13 Loss: 0.0037 Acc: 0.9946 : 100%|██████████| 100/100 [00:05<00:00, 24.82it/s]\n", + "Epoch: 14 Loss: 0.0073 Acc: 0.9905 : 100%|██████████| 1000/1000 [01:52<00:00, 8.89it/s]\n", + "Test : 14 Loss: 0.0042 Acc: 0.9945 : 100%|██████████| 100/100 [00:05<00:00, 17.40it/s]\n", + "Epoch: 15 Loss: 0.0019 Acc: 0.9971 : 100%|██████████| 1000/1000 [01:52<00:00, 8.99it/s]\n", + "Test : 15 Loss: 0.0034 Acc: 0.9957 : 100%|██████████| 100/100 [00:05<00:00, 17.38it/s]\n" + ] + } + ], + "source": [ + "optimizer = torch.optim.Adam(model.parameters(), 1e-4, amsgrad=True)\n", + "epochs = 15\n", + "for epoch in range(1, epochs + 1):\n", + " train(model, optimizer, epoch, train_loader)\n", + " valid(model, optimizer, epoch, valid_loader)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 测试模型输出" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T12:48:58.168479Z", + "start_time": "2019-06-18T12:48:57.536996Z" + }, + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true: DYE8\n", + "pred: DYE8\n", + "true: BMRV\n", + "pred: BMRV\n", + "true: 9NPM\n", + "pred: 9NPM\n", + "true: CCVM\n", + "pred: CCVM\n", + "true: QN7Z\n", + "pred: QN7Z\n", + "true: PGK1\n", + "pred: PGK1\n", + "true: 4SIU\n", + "pred: 4SIU\n", + "true: A662\n", + "pred: A662\n", + "true: KLUM\n", + "pred: KLUM\n", + "true: NOFK\n", + "pred: NOFK\n", + "true: MAIR\n", + "pred: MAIR\n", + "true: BOXU\n", + "pred: BOXU\n", + "true: OA18\n", + "pred: OA18\n", + "true: FQEK\n", + "pred: FQEK\n", + "true: UIED\n", + "pred: UIED\n", + "true: Y4MR\n", + "pred: Y4MR\n", + "true: SZXQ\n", + "pred: SZXQ\n", + "true: 5OND\n", + "pred: 5OND\n", + "true: 3HEP\n", + "pred: 3HEP\n", + "true: IKJ8\n", + "pred: IKJ8\n", + "true: LTWA\n", + "pred: LTWA\n", + "true: K5O7\n", + "pred: K5O7\n", + "true: 4R71\n", + "pred: 4R71\n", + "true: JL3Z\n", + "pred: JL3Z\n", + "true: ER9Z\n", + "pred: ER9Z\n", + "true: EZ1S\n", + "pred: EZ1S\n", + "true: EGKF\n", + "pred: EGKF\n", + "true: XF0X\n", + "pred: XF0X\n", + "true: Z8P4\n", + "pred: Z8P4\n", + "true: ADCK\n", + "pred: ADCK\n", + "true: B1K0\n", + "pred: B1K0\n", + "true: D8KG\n", + "pred: D8KG\n", + "true: XPTH\n", + "pred: XPTH\n", + "true: T1ZY\n", + "pred: T1ZY\n", + "true: 8WG5\n", + "pred: 8WG5\n", + "true: P7RV\n", + "pred: P7RV\n", + "true: 0HLH\n", + "pred: 0HLH\n", + "true: U0AG\n", + "pred: U0AG\n", + "true: 56PK\n", + "pred: 56PK\n", + "true: 6IJG\n", + "pred: 6IJG\n", + "true: 2FN2\n", + "pred: 2FN2\n", + "true: 7QNI\n", + "pred: 7QNI\n", + "true: OKZH\n", + "pred: OKZH\n", + "true: 1DI8\n", + "pred: 1DI8\n", + "true: 62T2\n", + "pred: 62T2\n", + "true: 85ET\n", + "pred: 85ET\n", + "true: PDBO\n", + "pred: PDBO\n", + "true: 0MJD\n", + "pred: 0MJD\n", + "true: U9YB\n", + "pred: U9YB\n", + "true: 6ZOK\n", + "pred: 6ZOK\n", + "true: B5PR\n", + "pred: B5PR\n", + "true: A3MI\n", + "pred: A3MI\n", + "true: X39Z\n", + "pred: X39Z\n", + "true: SVRY\n", + "pred: SVRY\n", + "true: 96L9\n", + "pred: 96L9\n", + "true: 2EL3\n", + "pred: 2EL3\n", + "true: VT0O\n", + "pred: VT0O\n", + "true: QWC5\n", + "pred: QWC5\n", + "true: OP3I\n", + "pred: OP3I\n", + "true: 570W\n", + "pred: 570W\n", + "true: OR0F\n", + "pred: OR0F\n", + "true: X65U\n", + "pred: X65U\n", + "true: 7W02\n", + "pred: 7W02\n", + "true: QK4Y\n", + "pred: QK4Y\n", + "true: SU5B\n", + "pred: SU5B\n", + "true: 1WK1\n", + "pred: 1WK1\n", + "true: M1K0\n", + "pred: M1K0\n", + "true: NYVL\n", + "pred: NYVL\n", + "true: ZQTO\n", + "pred: ZQTO\n", + "true: IL3Z\n", + "pred: IL3Z\n", + "true: VGEL\n", + "pred: VGEL\n", + "true: 89NK\n", + "pred: 89NK\n", + "true: EFW8\n", + "pred: EFW8\n", + "true: RR68\n", + "pred: RR68\n", + "true: PKIS\n", + "pred: PKIS\n", + "true: 5OA9\n", + "pred: 5OA9\n", + "true: SWTO\n", + "pred: SWTO\n", + "true: F4GT\n", + "pred: F4GT\n", + "true: MMHS\n", + "pred: MMHS\n", + "true: 5FGG\n", + "pred: 5FGG\n", + "true: VKNL\n", + "pred: VKNL\n", + "true: F84U\n", + "pred: F84U\n", + "true: EK0H\n", + "pred: EK0H\n", + "true: 1LNW\n", + "pred: 1LNW\n", + "true: GIYU\n", + "pred: GIYU\n", + "true: UHEI\n", + "pred: UHEI\n", + "true: V7XJ\n", + "pred: V7XJ\n", + "true: SWA9\n", + "pred: SWA9\n", + "true: S7AL\n", + "pred: S7AL\n", + "true: UKV3\n", + "pred: UKV3\n", + "true: 5NON\n", + "pred: 5NON\n", + "true: 2QF3\n", + "pred: 2QF3\n", + "true: 5891\n", + "pred: 5891\n", + "true: R7SM\n", + "pred: R7SM\n", + "true: U0AD\n", + "pred: UOAD\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.eval()\n", + "do = True\n", + "while do or decode_target(target) == decode(output_argmax[0]):\n", + " do = False\n", + " image, target, input_length, label_length = dataset[0]\n", + " print('true:', decode_target(target))\n", + "\n", + " output = model(image.unsqueeze(0).cuda())\n", + " output_argmax = output.detach().permute(1, 0, 2).argmax(dim=-1)\n", + " print('pred:', decode(output_argmax[0]))\n", + "to_pil_image(image)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2019-06-18T12:49:28.691803Z", + "start_time": "2019-06-18T12:49:28.645668Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ypw/anaconda3/lib/python3.6/site-packages/torch/serialization.py:256: UserWarning: Couldn't retrieve source code for container of type Model. It won't be checked for correctness upon loading.\n", + " \"type \" + obj.__name__ + \". It won't be checked \"\n" + ] + } + ], + "source": [ + "torch.save(model, 'ctc.pth')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/ctc_pytorch.ipynb b/ctc_pytorch.ipynb index ca617f5..285d7a4 100644 --- a/ctc_pytorch.ipynb +++ b/ctc_pytorch.ipynb @@ -116,14 +116,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "67NQ tensor([12]) tensor([4])\n" + "H14H tensor([12]) tensor([4])\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "execution_count": 3, @@ -159,8 +159,8 @@ "batch_size = 128\n", "train_set = CaptchaDataset(characters, 1000 * batch_size, width, height, n_input_length, n_len)\n", "valid_set = CaptchaDataset(characters, 100 * batch_size, width, height, n_input_length, n_len)\n", - "train_loader = DataLoader(train_set, batch_size=batch_size, num_workers=12)\n", - "valid_loader = DataLoader(valid_set, batch_size=batch_size, num_workers=12)" + "train_loader = DataLoader(train_set, batch_size=batch_size, num_workers=0)\n", + "valid_loader = DataLoader(valid_set, batch_size=batch_size, num_workers=0)" ] }, { @@ -221,7 +221,7 @@ " x = x.permute(2, 0, 1)\n", " x, _ = self.lstm(x)\n", " x = self.fc(x)\n", - " return x" + " return x.permute(0,1,2) # setting batch-size to first dim makes it easy to change code of multi-gpu training " ] }, { @@ -401,7 +401,8 @@ " optimizer.zero_grad()\n", " output = model(data)\n", " \n", - " output_log_softmax = F.log_softmax(output, dim=-1)\n", + " # shape[1]=batch,shape[0]=input_length\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2) # swap batch and input_length dimension\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss.backward()\n", @@ -428,7 +429,7 @@ " data, target = data.cuda(), target.cuda()\n", " \n", " output = model(data)\n", - " output_log_softmax = F.log_softmax(output, dim=-1)\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2)\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss = loss.item()\n", @@ -870,7 +871,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.7.3" } }, "nbformat": 4, From 59e3f95f14fef9b7a51c902c457fa233848ab63c Mon Sep 17 00:00:00 2001 From: ChenYuyang Date: Sun, 19 Apr 2020 16:39:41 +0800 Subject: [PATCH 2/6] =?UTF-8?q?pytorch=20=E6=B7=BB=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E5=8D=A1=E8=AE=AD=E7=BB=83=EF=BC=8C=E4=BF=AE=E6=94=B9=E8=AF=86?= =?UTF-8?q?=E5=88=AB=E5=A4=A7=E5=86=99=E5=AD=97=E6=AF=8D=E4=B8=BA=E8=AF=86?= =?UTF-8?q?=E5=88=AB=E5=A4=A7=E5=B0=8F+=E5=B0=8F=E5=86=99=E5=AD=97?= =?UTF-8?q?=E6=AF=8D.=20Add=20multi-gpu=20training=20code.=20Change=20reco?= =?UTF-8?q?gnizing=20uppercase=20letter=20to=20uppercase=20+=20lowercase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctc_pytorch_multigpu.py | 295 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 ctc_pytorch_multigpu.py diff --git a/ctc_pytorch_multigpu.py b/ctc_pytorch_multigpu.py new file mode 100644 index 0000000..81f9f9a --- /dev/null +++ b/ctc_pytorch_multigpu.py @@ -0,0 +1,295 @@ +# -*- coding: utf-8 -*- +"""ctc_pytorch.ipynb + +Automatically generated by Colaboratory. + +Original file is located at + https://colab.research.google.com/drive/1il6A-3w1U_YOPN3xHLWm54yj3iapNqpW + +# 导入必要的库 + +我们需要导入一个叫 [captcha](https://github.com/lepture/captcha/) 的库来生成验证码。 + +我们生成验证码的字符由数字和大写字母组成。 + +```sh +pip install captcha numpy matplotlib torch torchvision tqdm +``` +""" + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import Dataset, DataLoader +from torchvision.transforms.functional import to_tensor, to_pil_image + +from captcha.image import ImageCaptcha +from tqdm import tqdm +import random +import numpy as np +from collections import OrderedDict +import string +import argparse +print("Tips: you should run multi-gpu training code using follow command:/n"+"python -m torch.distributed.launch --nproc_per_node=NUM_GPU_YOU_HAS ctc_pytorch_multigpu.py") + +parser = argparse.ArgumentParser() +torch.distributed.init_process_group(backend="nccl") +parser.add_argument("--local_rank", type=int) +args = parser.parse_args() +torch.cuda.set_device(args.local_rank) + + +characters = "-" + string.digits + string.ascii_letters +width, height, n_len, n_classes = 192, 64, 4, len(characters) +n_input_length = 12 +print(characters, width, height, n_len, n_classes) + +"""# 搭建数据集""" + + +class CaptchaDataset(Dataset): + def __init__(self, characters, length, width, height, input_length, label_length): + super(CaptchaDataset, self).__init__() + self.characters = characters + self.length = length + self.width = width + self.height = height + self.input_length = input_length + self.label_length = label_length + self.n_class = len(characters) + self.generator = ImageCaptcha(width=width, height=height) + + def __len__(self): + return self.length + + def __getitem__(self, index): + random_str = "".join( + [random.choice(self.characters[1:]) for j in range(self.label_length)] + ) + image = to_tensor(self.generator.generate_image(random_str)) + target = torch.tensor( + [self.characters.find(x) for x in random_str], dtype=torch.long + ) + input_length = torch.full( + size=(1,), fill_value=self.input_length, dtype=torch.long + ) + target_length = torch.full( + size=(1,), fill_value=self.label_length, dtype=torch.long + ) + return image, target, input_length, target_length + + +"""# 初始化数据集生成器""" + +batch_size = 128 +train_set = CaptchaDataset( + characters, 1000 * batch_size, width, height, n_input_length, n_len +) +valid_set = CaptchaDataset( + characters, 100 * batch_size, width, height, n_input_length, n_len +) +train_sampler = torch.utils.data.distributed.DistributedSampler(train_set) +valid_sampler = torch.utils.data.distributed.DistributedSampler(valid_set) +train_loader = DataLoader( + train_set, + batch_size=batch_size, + num_workers=64, + pin_memory=True, + sampler=train_sampler, +) +valid_loader = DataLoader( + valid_set, + batch_size=batch_size, + num_workers=12, + pin_memory=True, + sampler=valid_sampler, +) + + +"""# 搭建模型""" + + +class Model(nn.Module): + def __init__(self, n_classes, input_shape=(3, 64, 128)): + super(Model, self).__init__() + self.input_shape = input_shape + channels = [32, 64, 128, 256, 256] + layers = [2, 2, 2, 2, 2] + kernels = [3, 3, 3, 3, 3] + pools = [2, 2, 2, 2, (2, 1)] + modules = OrderedDict() + + def cba(name, in_channels, out_channels, kernel_size): + modules[f"conv{name}"] = nn.Conv2d( + in_channels, + out_channels, + kernel_size, + padding=(1, 1) if kernel_size == 3 else 0, + ) + modules[f"bn{name}"] = nn.BatchNorm2d(out_channels) + modules[f"relu{name}"] = nn.ReLU(inplace=True) + + last_channel = 3 + for block, (n_channel, n_layer, n_kernel, k_pool) in enumerate( + zip(channels, layers, kernels, pools) + ): + for layer in range(1, n_layer + 1): + cba(f"{block+1}{layer}", last_channel, n_channel, n_kernel) + last_channel = n_channel + modules[f"pool{block + 1}"] = nn.MaxPool2d(k_pool) + modules[f"dropout"] = nn.Dropout(0.25, inplace=True) + + self.cnn = nn.Sequential(modules) + self.lstm = nn.LSTM( + input_size=self.infer_features(), + hidden_size=128, + num_layers=2, + bidirectional=True, + ) + self.fc = nn.Linear(in_features=256, out_features=n_classes) + + def infer_features(self): + x = torch.zeros((1,) + self.input_shape) + x = self.cnn(x) + x = x.reshape(x.shape[0], -1, x.shape[-1]) + return x.shape[1] + + def forward(self, x): + x = self.cnn(x) + x = x.reshape(x.shape[0], -1, x.shape[-1]) + x = x.permute(0, 2, 1) + x, _ = self.lstm(x) + x = self.fc(x) + return x + + +"""## 测试模型输出尺寸""" + +model = Model(n_classes, input_shape=(3, height, width)) +model = model.cuda() +model = torch.nn.parallel.DistributedDataParallel( + model, device_ids=[args.local_rank], output_device=args.local_rank +) + +"""# 解码函数和准确率计算函数""" + + +def decode(sequence): + a = "".join([characters[x] for x in sequence]) + s = "".join( + [x for j, x in enumerate(a[:-1]) if x != characters[0] and x != a[j + 1]] + ) + if len(s) == 0: + return "" + if a[-1] != characters[0] and s[-1] != a[-1]: + s += a[-1] + return s + + +def decode_target(sequence): + return "".join([characters[x] for x in sequence]).replace(" ", "") + + +def calc_acc(target, output): + output_argmax = output.detach().permute(1, 0, 2).argmax(dim=-1) + target = target.cpu().numpy() + output_argmax = output_argmax.cpu().numpy() + a = np.array( + [ + decode_target(true) == decode(pred) + for true, pred in zip(target, output_argmax) + ] + ) + return a.mean() + + +"""# 训练模型""" + + +def train(model, optimizer, scheduler, epoch, dataloader): + model.train() + loss_mean = 0 + acc_mean = 0 + with tqdm(dataloader) as pbar: + for batch_index, (data, target, input_lengths, target_lengths) in enumerate( + pbar + ): + data, target = data.cuda(), target.cuda() + + optimizer.zero_grad() + output = model(data) + + output_log_softmax = F.log_softmax(output, dim=-1) + output_log_softmax = output_log_softmax.permute(1, 0, 2) + loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths) + + loss.backward() + optimizer.step() + scheduler.step() + + loss = loss.item() + acc = calc_acc(target, output) + + if batch_index == 0: + loss_mean = loss + acc_mean = acc + + loss_mean = 0.1 * loss + 0.9 * loss_mean + acc_mean = 0.1 * acc + 0.9 * acc_mean + + for param_group in optimizer.param_groups: + pbar.set_description( + f"Epoch: {epoch} Loss: {loss_mean:.4f} Acc: {acc_mean:.4f} Lr: " + + str(param_group["lr"]) + ) + break + + +def valid(model, optimizer, epoch, dataloader): + model.eval() + with tqdm(dataloader) as pbar, torch.no_grad(): + loss_sum = 0 + acc_sum = 0 + for batch_index, (data, target, input_lengths, target_lengths) in enumerate( + pbar + ): + data, target = data.cuda(), target.cuda() + + output = model(data) + output_log_softmax = F.log_softmax(output, dim=-1) + output_log_softmax = output_log_softmax.permute(1, 0, 2) + loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths) + + loss = loss.item() + acc = calc_acc(target, output) + + loss_sum += loss + acc_sum += acc + + loss_mean = loss_sum / (batch_index + 1) + acc_mean = acc_sum / (batch_index + 1) + + pbar.set_description( + f"Test : {epoch} Loss: {loss_mean:.4f} Acc: {acc_mean:.4f} " + ) + + +optimizer = torch.optim.Adam(model.parameters(), 1e-3, amsgrad=True) +scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=100, gamma=0.98) +epochs = 30 +for epoch in range(1, epochs + 1): + train(model, optimizer, scheduler, epoch, train_loader) + valid(model, optimizer, epoch, valid_loader) + torch.save(model, "ctc.pth") + + +from PIL import Image + +model.load_state_dict(torch.load("ctc.pth").state_dict()) +model.eval() +iamge = Image.open("rancode.jpg") +output = model(image.unsqueeze(0).cuda()) +output_argmax = output.detach().permute(1, 0, 2).argmax(dim=-1) +print("pred:", decode(output_argmax[0])) + +# torch.save(model, "ctc.pth") From 2f2616a09515fee0a87348d4949bbc44c4d988cd Mon Sep 17 00:00:00 2001 From: ChenYuyang Date: Sun, 19 Apr 2020 16:40:53 +0800 Subject: [PATCH 3/6] work 0->12 --- .ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb | 11 ++++++----- ctc_pytorch.ipynb | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb b/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb index 70fa945..7891715 100644 --- a/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb +++ b/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb @@ -116,14 +116,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "67NQ tensor([12]) tensor([4])\n" + "H14H tensor([12]) tensor([4])\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAABACAIAAADDDu+IAAAcz0lEQVR4nO19229kyXnf91Wdc6rO6Sabl+GQc7/s7M7Maseza0HSroVdJDaCWPAmCAIoAmL4IfBL4Pw9yZuRB0MPhoEAQSToyRKwgiSvJUdajay9zWqHM7MzHA6b7MO+nLpXHqrvbDZPk+xZwcgPDaLZfS5Vdb767t/XqJ2F/49/ifjmX/7FT/76b05yQBngXAnIOVdIUQghtAbvU84zxlPOS56et5rhTa26MLcx/n5BSFlI0RHCew+IYblSxspfIW81H249/e9/97cAcGXj3F99+ztjq3fkATMhmunoQgohJQCkjPMSs7LWPq/X//mz+7utpgNYrdXeunO3JAGFeX7vx++9+/Y7lzfmQkO/hwQqlPzlh799Wq8b7wjA2tLK11+7MxMBAcD3fvxeePPu2+/MesCsazKNgKy1hZSFEMpo7z0AFFL+9rP71TT96quvlSEg51yn6Dxv7D7d3XXhCkJA7ehhDe+S7/34vb/69ncOO1IbrbUGgDiOwYPWWhmTxFEcxXEcH3mLuRLoMaCUypvNJ8+3lTUEkSIppDzyLOectVZIKbVqdjp//LVv/PDn77/79juXN85NnNe7b7/Tm/jIAcdYk2kEZKx9uvXs44cPGp2W9R4ApJTWmLNLy6LErAAAERCxzJEHMbxLjLXGWgCIKO2OzZjwV2n16MkjD379zAYAfPF0SyhJKFlfO7tYrXI2mf+XJ9AXDASMCA2L5j04DwD+yLOcc612ay/PP9p8UGh1+dz5f//Ov0o5T6IJW6hWXbi8AWHKB6ln1jWZyoGMbXfaz3frz/KG9Q4AKCFZFJPSNCG1VsYobUoeP4ywS/7tW3907swai+LtnZ2Mc8aY9z6J40KIvcZeHCdPn23lrYZQ6unzHaF0RGi7KPZF8dHm5mJ14Ss3bqzUahMVrylsvBCiIwUAzKSxnQR9wYGIiNhfYQ/+aPIB8N4TJFLret7YbTWfNxoZ56u1Wq1anTj+w1jLkbLvIGbQgRCAAGacp4yVkV8AILX+3RdfSKXK3yWgv0uKQrT2m4qxZrtd39sD8LXFxVarlTDWbjb3mvv1PN9vt/bbrY8fPbx5+Yr1AADOe+3sbqv5bK++UqvdfeVWxtiYCn8YGy+EqOeNn937NYvjt/7gLksSQsis458Jw4KjytKYdjkQIiACwtHbFRGREEooIaSQslUUuzlgaYWhj8PWZAqmEZA2WhmjTY9/IFKCnLFbL90oSUBKKamUmJ2AoLdLvPP1en3r+TYhRGhttH745ElbKx5FzaJgUbRVr9f3Gx98dh8A8qK4eeUqDc8bsS2KtijaUtTzPGU84+yNm7crPM04Z0lyeePcQTbeEaKeN3748/d39/PFrLJdry9VF+ZKQMOC43/96O//87/5Uw8+cCAEjAktw+49gPc+nEgAg11dCFFIWUhZUgc/TLRNx1QCsuZpfUf1CAgBECDlKU9KcSDnnHXOOnfYAaUUfu/zZlNI4T3stZp50YkpzdttoVVMI+OsNubjx4+0tR789TNrznlKRjZtIUQhBACkjOf7zbXF2upS7c7N27WF8ZsKKff28x/94h/reV5IUWHcWmudjWGaMn5y9AXHn775za3tbedcIFlECARx5BUIIiEkojSJIiQIDjxAR8rffPrpam2pvBF3DEviUAKy1hZCdoRQxviBHoflhDJAzwSzzvpJemB5hZ8lyd5+LrXuSLHf6QSOaKzVxgKAMvr82tlH21uXzm5U06yrNnrwOH5PIYTVxioVUSKkPEhAhZS//PC3u3mjkAIAPIB1toQKe1L0BcdSZSHPG+2iM6vlgQSzNK1WKixOKHb5ZSFEIUUZI+4kOJQ5G2v3Gg1ltHYm0Mys1pRzzjtvnbXejZFdn29vbj3t77+JYEnCkrhWqcRRtF8U0ijtTLDIPHqPPo6jSpreunKtkqZJFCFAIFf0iH5kyIhIDp+DtbbVbtf388GKew9+7vRTqy4EYXp549zy4mJEaUSjvtAseX+ChFIaR1ESJz3iw9mf2HFwOAcy1lprjHHed6eBGJHyFhgABtkcxPM4Sir8URxfvniJEKKci+PIFg5gnLskcQy9BUOAiFDbvaUHwP4z6L8RUkolhZTDgriQcmvneUcI1xus89455w8XwaeFPvdtdzoxjePuNgDvvVJaaqW0Tqb6tAIIIZSQrgr4AjhnuOkR32OXkIMJhkjwyFNGTj50H7z79jtXNs79t29/Z7rCH0dRmqbXLl+5dvlyynnc8wON3gYJAEWMKa2m6Wpt6cKZtZWFBRbFBHHUikEAkEp98vnnw7ahUmq/2fzs0cOOKBCAICKic67daWtjrH1B4UIEDB4vbU2Yl9J68/EXqpwVEtEoS9Mo6jOFL5UDKaOV0UrrrkRAjAhJOeOlbfiuJdp9O7IhZlL44yiKo6hWXbiweqaeN3TvcYYrUiQVnjJKojimhKzWll596YYx5v7mg/uPHxGHrsd6PATlcsCB+mqQ1OqTz3/XKjpKG4IY00hba6wRUtX3dtM0pZMIt49TDIkoox9uP+t7zgophZKFlNVK5chzoyhaXKhOH+qp41AC0sY8er4t9YD2ETFl/PZLL5V1AikVXoMr9P/MvtYp52u1pdWFRe/zYBhSQiNCEPHc8vKNK1d28/zqhYsrtVrK+H672dESekKtT7zOO+sd9eNMVCsttWoVRaAeStB5Z6zbbe5Lpa21cLgEOWgNnISegt9EGQ2B4r1rS+F8KTFKSRfd/7uCfr6ybDIBWWsLKTqikLobBQvCKOW8DAcKK7iz1/j00cM+CQ5k4bEQR9GlCxe995tbT7cae+D9cnWBIBLE5Vrtwtn129dvAEAY2+5+w1ijrRlTQr3zhVIRH9+jznvvwToHiDyOEFE7a60VSimtpxieB93/Jw+x+aF31jnrnStn+QadHwf/djnuXDGZgIy1e3sNY+yw+VRSre+v4Ouv3NzerY9xoGOL5SiKFiqVl6+/dHZt7f7DzSSK18+c2d7Z8d7fuHqtWqmwJBkc7ZF4al3PwdZDYD8HN7TzviWE88FsQ4KEIjFglDHWuemm0LA1kLdaeat5whAbYo9vIgAAhdJ2iwc3rPK/EDX6EA4UTDBrBqvfdasfgf6OdM4+3Hry6tXr3k90Ax1rrF3Qxd62PrO8AgBJkowbKQjOewBvnR8d9AHvEAAAGGets8bZCAkCdJ2fHpTRhSykUixhh+kWw+5/APjuD77f/3ym2VlrrTUuaHg9+qHTPA+Hoi+1X4AWfagONLbQYTJlrhh2pHPuT7769af1537gesRjR+aHkcRJEidHHoZRT3X2fV0eeo6h8WF474NuzpMkiSMHILTy4I219UajvrtbSbOJBDRmDeSt5vRw0hT1yFi718ittcGR7sOeLRcL64OM+Fkm+k9KDWaGO078tGeC9YMYSBA5Y2XyyIJ9/l//43+6uL5ezSr9ORy2DHmrGV7Hm8BEBHcIpdQj+qEV7SthfUMMAIwxWhtlNEGMo+jly1crPGUsAQ/aGCGlNmZKQKZWXQgvGPUKTqSeh1tP/8ff/e3DracH52uNtTb4SNH3hjqjQzooCUMUdDjznz6Y8phAQNbaQojPnz6RWncHhhATWkn5zevXeTKNgAYreO7cUgm6Pq1pjCGidKVS43E8yW807grSxmzv7GhjCJIzizVG6Ru3X+UJ6xlB0lpX/kEO09MwSjrfvffGucA1Qxx1Bme4B+89luD05SMBR2ICARlrn+3sdBMRAaDHSzkrZYKF5etSzxAP5ZyljKVskJ5yitMYAyWkytn55ZXoAAF57533HSH6HEhrrYzWWhPEiNLLFy+lPE0SBgDWOeusNMo6607skj7S+a6NUcYoo31X2PYCM+VICBERgfa0puknHSP1ZyImcaCuBm1HaH9MGJTE0Bkp43devpnyEfo7rWmMgSDhccySJInGlTzvfaH1sGFsnZNKOufiKIrjhDHWdeZ6DwAOAAFLZmBOx3TnO42o8/C8sRuSLX2X1mcxxRHjOCE4yEboCBGSOmYdTHmUSijDPlv0ZUlIaCW1Hs4ECtUFwxwIjpXBVAaEYJIwNogsDmCdo2TgWTHGCCnzZksbs1ipbqyeYUniEXiSpJwLJRExjuOT5yUe6XyPKK0uVAExxDEC2XCWzOT6J0iQIPbMsELK39yfkNFxvNSfiZhAQCGCIXvyqzs4RD+LPJZKffrFoxFHNgwYUl/dmZjVdXIgkiRJKI1wguU4kiZqrd3eeb5ftJ33GePra2ucMULI3Vdu1vOGUDKhNCSMnjytbPocKaVRRKMoGtaCU8ZvXy+bvgcA3rthMyzUVE3kQKe14BMWRRm9ub01pEEjIgI6h7a8PyewHzHkhiZD1NNXnKGnM51sFuOIoiiOIsaTg0K394EPhlijud8RolUUlJBqmrIkppSmnIcXABhr/VG+xNMCApLRBDLOWPkE4hA1O0YC8UkwzoGstR0hOlIorfuxq5hQ532r6Ozke9YPYpkZ4ynjGecQxK0UHSHCt7t5XkgBfsSnhS+qHCKidGV5eWu3fpAD9RW5YIhdv3Rpe2/PGBMTUk05HWUz3nttjXXu5Bp0GUxPgh5e4Z6raATtovinjz/MO+0XlcoBcJCAjLWNRj6WwGCdMw4arfbP7n3AGQsaHiJEUfaHN29WGAeAjix+9fHHhRTBhyGkLIQYTHJoTeakOA+DUhrRiCUTOFAfQsr9Vms3zwshrHWVSoUlCUsYDGUTewBtTKO5L5XiPKVzzq4PHKint3nnXbsodvK9UD1cCPGrTz5qtFrBKD5Y9dZNQeztYeimxMFcgxoHOJAJFpgd8HoPyloA0IVpFkWPLXnvMeX8h/uNCuMefCFl2CITbzPs4JqT4jx+R0SeMJ4wliRjXN15H17WmkdPnmhjACHj/PL5C+GptGXxq08/bMsCALSxu/v7u3uNaqVKCemXGMQH7LtTGXNEu6LeA1jv8077px/8ijEGAB0hGq3mzt7e4+1nl9Y3Kml6ZJaZB2+cLZ2EfBwcsgrD9vsY/XZJGqGXr75b7k6B8E5R/z/idgSrWXbzytXWRx+OEZD1rq1kxImQaqES550WIlbTLOW8/0hMt7QBjLNKqUIKa03H2nanU9/bu3Tu3DwIKATenRukRDaLTrPohH+V1u2i+HjzAQA82t66deVaycvOVYEb58nKaKm1NHp+tzzMV3u6IEhqCwu1apWz8cCZ8z64E7139TzX1iJiyhjpKUwZ51+98ZWg2znvpdGtTqfeaDzefvajn7+/tfP8VNxCY1BaCymf7+f28OyfR9tbgZlfOrsx9lUIN0WERITQgRwM3RrkYZLh5BjZRsbajhCfPfmib4IdA6p3bn83o8eU8fSQKuM5gRBCKaW9Ir1hICACsdYJpZS12phqlvEk6Rs7GUsrjGcs3YXcea+tbRWdf/j1r3eaudHaWXtDXtFaT6+9nxVa683Hj6VSU+Jul85uhPqTvvwKzpFg5CaEBi3NeS+MCQUlHSnuffrxmdrSmAfutDBCQNaYre3tQoquLwugl03f0+sAAHxEaURoiBKET0LmPIQUTCkfbD0JW6RPQynnr7388pg7bq7NMRAx6bplx7ls0Macc/X9fRLHBjyLk4vnz08kCO99odRO3thuNFqioEjSJHn05MlKbel0CSg4oPXhWzcsZpBcGecxpTGNQv0YAlKCFAklBAGk0daDNQoACiGEDLrpLDWqpTHKgYwx3YBwFwgQ0SiJaERoHMdxHEVxHBG6tliLewRknTXGKKX29pvPtf7owefWuzEhHXzQwxxo3s0xEJEQwpKEIKLH4UKO8D74oJv7+fLScpLEfDSpaNh4kUY/2nke3jvvTV9JOVWEbFRKRoJ3IQRAsJvXwaM4zCuJorOLSzGNws5OkjiJkziKgnDO2+0Hz58Jo6AfoZ8bRgjIe2+t7QdAvfeIuFhJL55Zr6ZpEsfnNjbiKI4ojSjtEpD3xlopxbNn2yyK/+mTDzPGCy2vnbswsgyjc3gx3qBenwIEgDEact5Z5zui0M4659JoQtCjb7wMirsBUs4Wq9Xrl6+cuhJNEFmcjJVNEURGo4hSSjCiURJFLEmqWbaQVXiSxLQ7hjiOz2+sx1FsjGm2Ws9261uNvSZ0YP5piSOroIwRWqmeBo2AGedrSytv3H61mmXxIU13rLX1XbuytGQBvvVHb7/3wf+9sr6Rd9qmJ8sRgJDxB/QCvEEA0FvA8WV03ktRGOec90CAjLah6RxwqAAAT5KM8zO15bfuvl6rVk9XfgEAIiFkJBODACYRrTDOoogSEtEoiaPlxcUzyyvra2eHU3h7siFWSiHAfqdNKe25Gr2bnld2MgwISGvdFsXD7WehZBgACJIKz16/dXt1eXm6N32pVqtUKmtra62iuHnt2j/f/1RtPe3bnyPJCT28GG9QtwqA8zFqcN4bBOkMIFIkgBhMsEA69UbjZ/c+GDa1KCFVnr7xyq2rFy8uVCpzUkiHEzcQMKIkS/ja0hKL4iSOkzjO0ixJouWlpaXFxSSZkJbZZbcAJJgKc3UBAcAwASmtNx8/7oghDRphMcvCM5hyiWDshPlUKpV8f38hy4YPOOibf2HeoJTx12/e3MkbHVEMD8M6G0r4KI1SxlYWF8McC1n89Ne/fPxsO3ii+8cTxDMLtZWFxaXqwsQnd3Ioo7U2/cVHhJhEKwuLK4u1hawSx/G59fUkjgkhURQdlqDte5XAOBTsKKTczfNjtFssgxEOpLRWQ1YAdos+Z1PBei6WI2j/xbSUy3g3WlfPR6ZhrW3kubUWkSRxfPHcec5YR4h6I3/0bGt3vzlG9AgotRqpMjhVhDqqB1tPhvQHIIhI8MrFi8uLtb6Qmn6drtI9qjEUQvziN/eerm9/7StHt1s8fo9E3zPHAUBpjQCe0GOo72OaOHg/TyOgHPxIHpNzTgdYR9AKIZTWzU673sh/eu8DIfXB8TrvjLVK6zlFVY212893CilDHnrw7jjvIko557XFxZLXQcQ4TpIkCfZ8gJBSK51xfmSnjmOYxgMfibFWGaOdDS7zjzYfOOcopeWTUbrXcVabEV/AlwsEGFPgrbWddkc5CwDOOVkUO7u7P/zH9//+5+/v5qE7BxIk4RVOcd4LraRS2ph50FDIAnX95GvEqJsQPaMd1aslD+X9M43heBnGAw5knN1tNwsp20Xx0ebnBPB53rhduhnZ4DrW7XXaZtB++ktwQ48hiIPQeRl63WeNsWGXFlK+f+9exOLOkL8/4yyNWKFlSxQA4L2XWu/u53t5I+V8Pj3LepbGgGUfrQmMwwdPkOvnsYT61JLDPYZpPLiytdY4Z6x9tL0FAIj4lWvXs9LZTAFSqY4sOkL0/fGImPL0oBv6RcMP+JDvR+Od9963C9EeasSUMr5aq106u/H67Vv9MXsAZfTeflPKaaGGkyAkAw2P2M+QUN87B0Jxqh/mu+WJ8BiJ0hO8YZfObjze3rq6cX4hzViJEr5hKKU+f/hwNPqNGZ+L/j8DepGW4UcUPnDeK2+dd8HXEFFaq1TevHN348yZQogHW0+bRSGkAABjnXV2eqn8CaGMDjVoPEm8x2PchyASgpTSaCgIWFKUnU6PxCSOEeHW1WsR0jJNjcbQ68gxUNa61sSsFzo99PNTCyEAwTnnnDPOegBEbxFcrwkBIi5mlZcvXLy4vl6rLnSYeP2Vm/VGHgjIe2+9R4Jz6rmptO4UxYcPfndhbZ0g0lB/NyMNIWKWZQtScsb6lcTeA2CpKx3DNJ6wFnEUszhhcQyzjN85ZwLsaF9BnHs4Zjoyxm9ff4lSEnJUrLXtTmGtBezah30LrcL5mVrt1vWXMsZhyAUAACnny4uLy7Xa2TNr82jA0yo69f38F5981OoUQYXoYsbiVEJIRCOWsIzxoZo4b33JDh8zY4IICyPmnIeM7pIXCv28mu2WtSO596fYXGEijvRbKKPbRefjzc2UM0R01goptHW+34TAB71naW1p6c07dxeqC313S3h84du37txdrdUyxudBQNqan/zmg9DPNCQyIECwPGY1YgAgjqJqlvWjdb1ymrk8iMkRQQRMGXt1FhPMeWesMTZYuaNdeU46yENxpN8iHPA//8//rucNgnjr6jXnvXHWeQfYbaiTcrZaW/rjr31jtVbLDuhqISD41p27l86uz88OMMZeOrv++ZMvrp2/UEnTJIoRQjuvma1gCCVmlcpYVe6cegVNJiBSLogxjOAD9QDWO9vLdwiG8zykV95q5q3W5tbT0E5lOKQ/xpO+9+P3QmT05tVrgyXs5jd5JLiYVd554w8vnl3PDtIHYpZmKeertdpcrUiWxLWs+uq169rapKc8VNL0eByIEEII4pCuNj9v7kQRhqT3mulaxhip1V673e/gdMA0PR0EvvLdH3x/s1dZ1vdbjPEkAHj37XfqeePy+jlCMIniwtjQdMcDUEp5krx04dJipTqBegAyxlZrtZcvX87mEz3tAwFDuLT/1Hv77jjLd0hj3LngYLZe6FE6u6GBoLTZ/OKJUHJYYUtPO4Y37DAFgGG/xUFfaugW8l/+3X84u7zMohgAEIH20iZiQtZXVnmSRNFktSbl/M07d+bNfiC0XyKDmnbsdic+LhFM+I2WeZHUQQ6EvluSPYP8AgAE1MaEpsZDH0LK2Ws3TtmL2CeOP//Wn13ZOFerVvvaz0Ffaq26YKzdWF3T1hZCUEpDd07vgSWMxckUpfiF+a4ceAcDS8mBI76bmHEMBFuMkn5KEMCcVOjDrLCZurFC37cbGtuMDBOrPDuomZ4QU3KJJn6VMX735is7+V5HFISQpaWacdYBrK2svOCmuBMhlSqk3Gs1+6LfT/qphvIghCzVFsNvyiAhGeMZ56u1pWwO+2ECAVFCqjNq0M45KeXBbqI9Dfo09aApDtPDvko5TxlPOYPcU0qSJDm7tgYAEaWEEl8i+WSuUFrd39wUciD6cfZQ6DAIIZU0fePmLUAkBO++citjPOVsHoJ4hID6/j4yo/EUAnjdRKAeC+rmxc0BUxymU77CnsswZMCFD733wuh5edmGMMVfpZR21qrxegwUSuWtVpqmPEnYjFlsKeMX1teXazVCyFx/Nu+AEt0r4Jn1yQcb3h1Q1r70XKCAASMcHZ7z3o00UpsLpnfyC6kXIyzHA3gvlbp3//69+/eP8YNrhJBKmq0tr6zWluZqAYwQUOBAGeOzatAAAIgeQFnbD1UHHvZlyoZx4Cn6Fcr3Bj0yzwYRY3rgh+UIivBzfXP+vaYTYpwDEcSUz+aDBgBAVFqH2fY628PcFP9j4hR54ay9Qafn2XR/oGDYdYKhIi+OCX350iU+nyzsU8EBfw/CQpqFH1UpfxUEkErdf/Swz2wD7fD5JHIfAyEgn7EUZxbO4zhG5t70PBttLSCO1HMiJDTy1rIkZrMrQKeOKex2oESnnK/UapSQ5VrtGGUr4bdRx/pgcHb6TqDjIWP8rTt3/8F/0JESRlW8Y/gJZ8rcOzLPxljzZHdHDxEQ6f3cwQuoyzkS0wOOAwLKGH/zzl0hZTaLAR+AiCnnq7Ulh9Auik6vKm/eqWTlSwhSzldh6V9//RtjBWLhq1kjFbMWtU0/RvW6+w7nEPp+hcOXqgYcWUM8woGOzSoQcXFh4Wuv3RFKtoX44JOPCikRYG1peX5RpFlLCLoTPHGLgVMvagtePoSBdKWIjEaU0NpCdXpn93kjb7W++4Pv563mcMBxGKdT4I2IGe/mXhVCrNZqhRCAOD8PxIuprj8Mp1vUljL+1h/cLYToZyiFZgqha9ExovGnhbzV3Nx6GmKOf/6tP5vIblG735f6m1nxzb/8i/DmJ3/9N1/uSP4F48hF/n/0RIllLgOjNgAAAABJRU5ErkJggg==\n", "text/plain": [ - "" + "" ] }, "execution_count": 3, @@ -401,7 +401,8 @@ " optimizer.zero_grad()\n", " output = model(data)\n", " \n", - " output_log_softmax = F.log_softmax(output, dim=-1)\n", + " # shape[1]=batch,shape[0]=input_length\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2) # swap batch and input_length dimension\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss.backward()\n", @@ -428,7 +429,7 @@ " data, target = data.cuda(), target.cuda()\n", " \n", " output = model(data)\n", - " output_log_softmax = F.log_softmax(output, dim=-1)\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2)\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss = loss.item()\n", diff --git a/ctc_pytorch.ipynb b/ctc_pytorch.ipynb index 285d7a4..7891715 100644 --- a/ctc_pytorch.ipynb +++ b/ctc_pytorch.ipynb @@ -159,8 +159,8 @@ "batch_size = 128\n", "train_set = CaptchaDataset(characters, 1000 * batch_size, width, height, n_input_length, n_len)\n", "valid_set = CaptchaDataset(characters, 100 * batch_size, width, height, n_input_length, n_len)\n", - "train_loader = DataLoader(train_set, batch_size=batch_size, num_workers=0)\n", - "valid_loader = DataLoader(valid_set, batch_size=batch_size, num_workers=0)" + "train_loader = DataLoader(train_set, batch_size=batch_size, num_workers=12)\n", + "valid_loader = DataLoader(valid_set, batch_size=batch_size, num_workers=12)" ] }, { From bd891c27f4ea4f9b3bc9e34006a9f6e24b36e8a2 Mon Sep 17 00:00:00 2001 From: Yuyang Chen Date: Sun, 19 Apr 2020 17:01:32 +0800 Subject: [PATCH 4/6] Update ctc_pytorch_multigpu.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在forward() 里面,LSTM之前的shape跟作者保持一致,不然训练不出来。 --- ctc_pytorch_multigpu.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/ctc_pytorch_multigpu.py b/ctc_pytorch_multigpu.py index 81f9f9a..312feab 100644 --- a/ctc_pytorch_multigpu.py +++ b/ctc_pytorch_multigpu.py @@ -157,10 +157,10 @@ def infer_features(self): def forward(self, x): x = self.cnn(x) x = x.reshape(x.shape[0], -1, x.shape[-1]) - x = x.permute(0, 2, 1) + x = x.permute(2, 0, 1) x, _ = self.lstm(x) x = self.fc(x) - return x + return x.permute(0, 1, 2) """## 测试模型输出尺寸""" @@ -282,14 +282,3 @@ def valid(model, optimizer, epoch, dataloader): valid(model, optimizer, epoch, valid_loader) torch.save(model, "ctc.pth") - -from PIL import Image - -model.load_state_dict(torch.load("ctc.pth").state_dict()) -model.eval() -iamge = Image.open("rancode.jpg") -output = model(image.unsqueeze(0).cuda()) -output_argmax = output.detach().permute(1, 0, 2).argmax(dim=-1) -print("pred:", decode(output_argmax[0])) - -# torch.save(model, "ctc.pth") From 6d87f2042fc2f59ce088431b2118bd36890c1573 Mon Sep 17 00:00:00 2001 From: Yuyang Chen Date: Sun, 19 Apr 2020 17:03:02 +0800 Subject: [PATCH 5/6] Update ctc_pytorch_multigpu.py --- ctc_pytorch_multigpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctc_pytorch_multigpu.py b/ctc_pytorch_multigpu.py index 312feab..5f7f285 100644 --- a/ctc_pytorch_multigpu.py +++ b/ctc_pytorch_multigpu.py @@ -160,7 +160,7 @@ def forward(self, x): x = x.permute(2, 0, 1) x, _ = self.lstm(x) x = self.fc(x) - return x.permute(0, 1, 2) + return x.permute(1, 0, 2) """## 测试模型输出尺寸""" From 02033ad8d409bf7217d4adc5339111677571b780 Mon Sep 17 00:00:00 2001 From: ChenYuyang Date: Sun, 19 Apr 2020 17:05:45 +0800 Subject: [PATCH 6/6] bugs fix --- .ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb | 6 +++--- ctc_pytorch.ipynb | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb b/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb index 7891715..e684ddc 100644 --- a/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb +++ b/.ipynb_checkpoints/ctc_pytorch-checkpoint.ipynb @@ -221,7 +221,7 @@ " x = x.permute(2, 0, 1)\n", " x, _ = self.lstm(x)\n", " x = self.fc(x)\n", - " return x.permute(0,1,2) # setting batch-size to first dim makes it easy to change code of multi-gpu training " + " return x.permute(1, 0, 2) # setting batch-size to first dim makes it easy to change code of multi-gpu training " ] }, { @@ -402,7 +402,7 @@ " output = model(data)\n", " \n", " # shape[1]=batch,shape[0]=input_length\n", - " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2) # swap batch and input_length dimension\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(1, 0, 2) # swap batch and input_length dimension\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss.backward()\n", @@ -429,7 +429,7 @@ " data, target = data.cuda(), target.cuda()\n", " \n", " output = model(data)\n", - " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2)\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(1, 0, 2)\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss = loss.item()\n", diff --git a/ctc_pytorch.ipynb b/ctc_pytorch.ipynb index 7891715..e684ddc 100644 --- a/ctc_pytorch.ipynb +++ b/ctc_pytorch.ipynb @@ -221,7 +221,7 @@ " x = x.permute(2, 0, 1)\n", " x, _ = self.lstm(x)\n", " x = self.fc(x)\n", - " return x.permute(0,1,2) # setting batch-size to first dim makes it easy to change code of multi-gpu training " + " return x.permute(1, 0, 2) # setting batch-size to first dim makes it easy to change code of multi-gpu training " ] }, { @@ -402,7 +402,7 @@ " output = model(data)\n", " \n", " # shape[1]=batch,shape[0]=input_length\n", - " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2) # swap batch and input_length dimension\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(1, 0, 2) # swap batch and input_length dimension\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss.backward()\n", @@ -429,7 +429,7 @@ " data, target = data.cuda(), target.cuda()\n", " \n", " output = model(data)\n", - " output_log_softmax = F.log_softmax(output, dim=-1).permute(0,1,2)\n", + " output_log_softmax = F.log_softmax(output, dim=-1).permute(1, 0, 2)\n", " loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n", " \n", " loss = loss.item()\n",