{"cells":[{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport torch\nimport torch.optim as optim\nimport torch.nn.functional as F\nimport os\nimport cv2\nimport torchvision\nimport sklearn.metrics\n\nfrom tqdm import tqdm\nfrom torch.utils.data import Dataset\nfrom albumentations import Compose, ShiftScaleRotate, Resize\nfrom albumentations.pytorch import ToTensorV2\n\nINPUT_PATH = '/kaggle/input/bengaliai-cv19'\nINPUT_PATH_TRAIN_IMAGES = '/kaggle/input/bengaliai/256_train/256'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from __future__ import print_function, division, absolute_import\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport torch.nn.functional as F\nimport os\n\n# Any results you write to the current directory are saved as output.\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset,DataLoader\nfrom torchvision import transforms,models\nfrom tqdm import tqdm_notebook as tqdm\nimport math\nimport torch.utils.model_zoo as model_zoo","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ======================\n# Params\nBATCH_SIZE = 32\nN_WORKERS = 4\nN_EPOCHS = 100\n\n# Disable training in kaggle\nTRAIN_ENABLED = False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Dataset\nFor training I use the 256x256 image dataset, you can find it [here](http://www.kaggle.com/dataset/a318f9ccd11aea9ede828487914dbbcb76776b72aeb4ef85b51709cfbbe004d3)."},{"metadata":{"trusted":true},"cell_type":"code","source":"class BengaliImageDataset(Dataset):\n\n    def __init__(self, csv_file, path, labels, transform=None):\n\n        self.data = pd.read_csv(csv_file)\n        self.data_dummie_labels = pd.get_dummies(\n            self.data[['grapheme_root', 'vowel_diacritic', 'consonant_diacritic']],\n            columns=['grapheme_root', 'vowel_diacritic', 'consonant_diacritic']\n        )\n        self.path = path\n        self.labels = labels\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n\n        image_name = os.path.join(self.path, self.data.loc[idx, 'image_id'] + '.png')\n        img = cv2.imread(image_name)\n\n        if self.transform:\n            transformed = self.transform(image=img)\n            img = transformed['image']\n\n        if self.labels:\n            return {\n                'image': img,\n                'l_graph': torch.tensor(self.data_dummie_labels.iloc[idx, 0:168]),\n                'l_vowel': torch.tensor(self.data_dummie_labels.iloc[idx, 168:179]),\n                'l_conso': torch.tensor(self.data_dummie_labels.iloc[idx, 179:186]),\n            }\n        else:\n            return {'image': img}\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model\nSimple resnet for now."},{"metadata":{"trusted":true},"cell_type":"code","source":"class SElayer(nn.Module):\n    def __init__(self, inplanes, reduction=16):\n        super(SElayer,self).__init__()\n        self.globalAvgpool = nn.AdaptiveAvgPool2d(1)#Squeeze操作\n        self.fc1 = nn.Conv2d(inplanes, inplanes // reduction, kernel_size=1, stride=1)\n        self.fc2 = nn.Conv2d(inplanes // reduction, inplanes, kernel_size=1, stride=1)\n        self.relu = nn.ReLU(inplace=True)\n        self.sigmoid = nn.Sigmoid()\n    def forward(self,x):\n        begin_input = x\n        x = self.globalAvgpool(x)\n        x = self.fc1(x)\n        x = self.relu(x)\n        x = self.fc2(x)\n        x = self.sigmoid(x)\n        \n        return x * begin_input\n                \n\nclass Bottleneck(nn.Module):\n    expansion = 4\n    \n    def __init__(self,inplanes,outplanes,stride=1,cardinality=32, downsample=None):\n        super(Bottleneck,self).__init__()\n        self.conv1 = nn.Conv2d(inplanes, outplanes*2, kernel_size = 1, bias = False)\n        self.bn1 = nn.BatchNorm2d(outplanes*2)\n        self.conv2 = nn.Conv2d(outplanes*2, outplanes*2,kernel_size=3,stride=stride,padding=1,\n                               bias=False,groups = cardinality)\n        self.bn2 = nn.BatchNorm2d(outplanes*2)\n        self.conv3 = nn.Conv2d(outplanes*2, outplanes*4, kernel_size=1, bias=False)\n        self.bn3 = nn.BatchNorm2d(outplanes*4)\n        self.selayer = SElayer(outplanes*4)\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n        self.stride = stride\n        \n    def forward(self,x):\n        residual = x\n        \n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        \n        x = self.conv2(x)\n        x = self.bn2(x)\n        x = self.relu(x)\n\n        x = self.conv3(x)\n        x = self.bn3(x)\n        \n        x = self.selayer(x)\n        \n        if self.downsample is not None:\n            residual = self.downsample(x)\n            \n        x += residual\n        x = self.relu(x)\n        \n        return x\n\nclass SEResNext(nn.Module):\n    \n    def __init__(self, block, layers, cardinality = 32,num_classes = 1000):\n        super(SEResNext, self).__init__()\n        self.cardinality = cardinality\n        self.inplanes = 64\n        \n        self.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3,\n                               bias=False)\n        self.bn1 = nn.BatchNorm2d(64)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n\n        self.layer1 = self._make_layer(block, 64, layers[0])\n        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)\n        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)\n        self.layer4 = self._make_layer(block, 512, layers[3], stride=2)\n        \n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        \n        self.fc = nn.Linear(512 * block.expansion, num_classes)\n        \n        \n        #参数初始化，待懂\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n                m.weight.data.normal_(0, math.sqrt(2. / n))\n                if m.bias is not None:\n                    m.bias.data.zero_()\n            elif isinstance(m, nn.BatchNorm2d):\n                m.weight.data.fill_(1)\n                m.bias.data.zero_()\n        \n    def _make_layer(self, block, outplanes, blocks, stride=1):\n        downsample = None\n        if stride != 1 or self.inplanes != outplanes * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.inplanes, outplanes * block.expansion,\n                          kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(outplanes * block.expansion),\n            )\n\n        layers = []\n        layers.append(block(self.inplanes, outplanes, self.cardinality, stride, downsample))\n        self.inplanes = outplanes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, outplanes, self.cardinality))\n\n\n        return nn.Sequential(*layers)\n        \n        def forward(self, x):\n            x = self.conv1(x)\n            x = self.bn1(x)\n            x = self.relu(x)\n            x = self.maxpool(x)\n    \n            x = self.layer1(x)\n            x = self.layer2(x)\n            x = self.layer3(x)\n            x = self.layer4(x)\n    \n            x = self.avgpool(x)\n            x = x.view(x.size(0), -1)\n            \n            x = self.fc(x)\n            \n            fc_graph = torch.nn.Linear(x.in_features, 168)\n            fc_vowel = torch.nn.Linear(x.in_features, 11)\n            fc_conso = torch.nn.Linear(in_features, 7)\n            \n            return fc_graph, fc_vowel, fc_conso\n        \n        \ndef se_resnext50(**kwargs):\n    \n    model = SEResNext(Bottleneck, [3,4,6,3], **kwargs)\n    return model\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"transform_train = Compose([\n    ToTensorV2()\n])\n\ntrain_dataset = BengaliImageDataset(\n    csv_file=INPUT_PATH + '/train.csv',\n    path=INPUT_PATH_TRAIN_IMAGES,\n    transform=transform_train, labels=True\n)\n\ndata_loader_train = torch.utils.data.DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    num_workers=N_WORKERS,\n    shuffle=True\n)\n\nmodel = se_resnext50().to(device)\noptimizer = optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n#scheduler = torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr=1e-4, max_lr=0.05)\ncriterion = nn.CrossEntropyLoss()\nbatch_size=32","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train"},{"metadata":{"trusted":true},"cell_type":"code","source":"# TRAIN_ENABLED is just for faster committing.\n# Feel free to remove it.\nif TRAIN_ENABLED:\n    for epoch in range(N_EPOCHS):\n\n        print('Epoch {}/{}'.format(epoch, N_EPOCHS - 1))\n        print('-' * 10)\n\n        model.train()\n        tr_loss = 0\n\n        tk0 = tqdm(data_loader_train, desc=\"Iteration\")\n\n        for step, batch in enumerate(tk0):\n            inputs = batch[\"image\"]\n            l_graph = batch[\"l_graph\"]\n            l_vowel = batch[\"l_vowel\"]\n            l_conso = batch[\"l_conso\"]\n\n            inputs = inputs.to(device, dtype=torch.float)\n            l_graph = l_graph.to(device, dtype=torch.float)\n            l_vowel = l_vowel.to(device, dtype=torch.float)\n            l_conso = l_conso.to(device, dtype=torch.float)\n\n            out_graph, out_vowel, out_conso = model(inputs)\n\n            loss_graph = criterion(out_graph, l_graph)\n            loss_vowel = criterion(out_vowel, l_vowel)\n            loss_conso = criterion(out_conso, l_conso)\n\n            loss = loss_graph + loss_vowel + loss_conso\n            loss.backward()\n\n            tr_loss += loss.item()\n\n            optimizer.step()\n            optimizer.zero_grad()\n\n        epoch_loss = tr_loss / len(data_loader_train)\n        print('Training Loss: {:.4f}'.format(epoch_loss))\n\ntorch.save(model.state_dict(), './seresnext_model.pth')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---------------\n**Thanks for reading. If you find this notebook useful, please vote**"},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}