{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install torchsummary","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install seaborn","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"scrolled":false},"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision\nfrom torchvision import transforms,datasets\nimport matplotlib.pyplot as plt\nimport torch.optim as optim\nfrom tqdm import tqdm_notebook as tqdm\nimport os\nimport numpy as np\nimport cv2\nimport zipfile\nreprocess_data = True\nfrom PIL import Image\nfrom torchsummary import summary\nimport time\nfrom torch.utils.tensorboard import SummaryWriter\nimport wandb\nimport seaborn as sns\nimport webbrowser\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Проверка есть ли возможность работать на GPU\ntorch.cuda.is_available()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Автоматический выбор девайса для работы: GPU или CPU\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n#Смотрим выбранный девайс\nprint(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Распаковываем архивы с обучающей и тестовой выборкой\n# ! unzip ../input/dogs-vs-cats/test1.zip \n# ! unzip ../input/dogs-vs-cats/train.zip ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"#Присваиваем переменным пути с обучающей и тестовой выборкой\ntrain_path = \"./train\"\ntest_path =\"./test1\"\n#Делаем списки из дерикторий\ntrain_files = os.listdir(train_path)\ntest_files = os.listdir(test_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Проверяем сколько строк в списках выборок\nprint(len(train_files))\nprint(len(test_files))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Формируем переменную пути для нулевого файла в обучающей выборке\nimgpath = os.path.join(train_path,train_files[0])\nprint(imgpath)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Читаем файл подгруженный в предыдущем шаге и конвертируем его в RGB пространство\nimg = cv2.imread(imgpath)\nimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\ntype(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Переводим изображение в многомерную матрицу (3 мерную так как изображение в RGB)\nimgtensor = torch.tensor(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Подгружаем размерность тензора (высота, ширина, глубина) для 0 изображения\nimgtensor.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"#Объявляем класс с тремя функциями \nclass Dataset():\n    def __init__(self,filelist,filepath,transform = None):\n        self.filelist = filelist\n        self.filepath = filepath\n        self.transform = transform\n    def __len__(self):\n        return int(len(self.filelist))\n    def __getitem__(self,index):\n        imgpath = os.path.join(self.filepath,self.filelist[index])\n        img = Image.open(imgpath).convert('L')\n        if \"dog\" in imgpath:\n            label = 1\n        else:\n            label = 0 \n        if self.transform is not None:\n            img = self.transform(img)\n        return (img,label)\n            \n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Ресайзим изображение до 60px\ntransformations = transforms.Compose([transforms.Resize((60,60)),transforms.ToTensor()])\n#Ресайзим обучающую выборку до 60px\ntrain = Dataset(train_files,train_path,transformations)\n#Ресайзим тестовую выборку до 60px\ntest = Dataset(test_files,test_path,transformations)\n#Разбиваем обучающую выборку (25к) рандомно на выборку обучения и валидации 20к и 5к\ntrain_set,val_set = torch.utils.data.random_split(train,[20000,5000]) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Проверяем сколько строк в валидационной выборке\nlen(val_set)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Делаем список из валидационной выборки, которую сплитнули на предыдущем шаге\nval_set_bal = [val_set.__getitem__(x)[1] for x in range(len(val_set))]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Визуализируем валидационную выборку (сколько файлов с собаками(1) и кошками(0))\nsns.countplot(val_set_bal)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"train set size :\",train_set.__len__())\nprint(\"val set size :\",val_set.__len__())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Проверили что \ntrain_set.__getitem__(0)[0].shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Формируем обучающий и тестовый датасеты по 16 картинок в батче\ntrain_dataset = torch.utils.data.DataLoader(dataset = train,batch_size = 8,shuffle=True)\ntest_dataset = torch.utils.data.DataLoader(dataset = test,batch_size = 8,shuffle=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"val_set.__getitem__(0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Объявляем класс модели\nclass Covnet(nn.Module):\n    def __init__(self):\n        super().__init__()\n        #Прописываем первый слой\n        self.conv1 = nn.Sequential(\n            #Объявляем первый сверточный слой где (conv1d - текст; 2d- изображения; 3d - трехмерное изображение)\n            #1- слой на вход, в данном случае тензор, 16-выходов, 3-размер ядра\n            nn.Conv2d(1,16,3),\n            #Функция активации\n            nn.ReLU(),\n            #Сжимаем с сохранением фичей для следующего слоя\n            nn.MaxPool2d(2,2),\n            #Боремся с переобучением (убираем 10% нейронов)\n            nn.Dropout(0.1)\n\n            ) \n            \n        self.conv2 =   nn.Sequential(\n            #Аналогично предыдущему, только на вход 16(тк в предыдущем 16 на выходе), 32 - на выходе (16*maxpool), 3-размер ядра(статичен)\n            nn.Conv2d(16,32,3),\n            nn.ReLU(),\n            nn.MaxPool2d(2,2),\n            nn.Dropout(0.1)\n\n        ) \n        self.conv3 =   nn.Sequential(\n            nn.Conv2d(32,64,3),\n            nn.ReLU(),\n            nn.MaxPool2d(2,2),\n            nn.Dropout(0.1)\n\n            )\n        self.fc1 = nn.Sequential(\n        #Переводим полученные фичи в вектор\n        nn.Flatten(),\n        #Создаем линейный слой 1600-входные нейроны (подбором) 256-выходных(степень двойки, подбор)\n        nn.Linear(1600,256),\n        #Функция активации \n        nn.ReLU(),\n        nn.Dropout(0.1)\n        )\n            \n        self.fc2 = nn.Sequential(\n        #Создаем финальный линейный слой с 256 нейронами на входе и 2 на выходе (2 тк у нас два класса: коты и собаки)\n        nn.Linear(256,2),\n        )\n                \n    def forward(self,x):\n        #Соединяем все слои в одно\n        x= self.conv1(x)\n        x= self.conv2(x)\n        x= self.conv3(x)\n        x = self.fc1(x)\n        x = self.fc2(x)\n        #Возвращаем все и в конце функция активации softmax для классификации\n        return F.softmax(x,dim = 1) \n        \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Объявляем переменную с классом модели (в шаге выше) \nmodel = Covnet()\nmodel.to(device)\n#Отображаем параметры итоговой модели\nsummary(model,(1,60,60))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Выбираем количество эпох(циклов прогона всех данных через модель)\nEPOCHS = 10\n#Объявляем переменную оптимизатора адам - по дефолту 0.001, подбирается исходя из результата\noptimiser = optim.Adam(model.parameters(),lr=1e-5)\n#Используем кросс энтропию(функцию потери) тк она лучше для классификации\nLOSS = nn.CrossEntropyLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(len(train_set))\nprint(len(val_set))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Объявляем переменную в которую будут суммироваться логи\nwriter = SummaryWriter()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for epoch in range(EPOCHS):\n    model.train()\n    total = 0\n    correct = 0\n    train_loss = 0\n    train_accuracy = 0\n    counter = 0\n    train_running_loss =0\n    with tqdm(train_dataset, unit=\"batch\") as tepoch:\n        tepoch.set_description(f'Epoch {epoch+1}/{EPOCHS}')\n        for data,label in tepoch:\n            data,label = data.to(device) , label.to(device)\n            optimiser.zero_grad()\n            output = model(data)\n            loss = LOSS(output,label)\n            loss.backward()\n            optimiser.step() \n            train_running_loss+= loss.item() * data.size(0)\n            _,pred = torch.max(output.data,1)\n            total += label.size(0)\n            correct += (pred == label).sum().item()\n        train_accuracy = correct/total\n        train_loss = train_running_loss/len(train_set)\n#         tepoch.set_postfix(accuracy =train_accuracy,loss = train_loss/len(train_dataset))\n        print(\"=====/train/=====\")\n        print(\"epoch {} train accuracy {}\".format(epoch+1,train_accuracy))\n        print(\"epoch {} train loss {}\".format(epoch+1,train_loss))\n        writer.add_scalar(\"train accuracy\",train_accuracy,epoch+1)\n        writer.add_scalar(\"train loss\",train_loss,epoch+1)\n#         wandb.log({\"train accuracy\":train_accuracy,\"train loss\":train_loss,\"epochs\":epoch+1},step = epoch+1)\n    if epoch %1 == 0:\n        model.eval()\n        total = 0\n        correct = 0\n        val_loss = 0\n        val_accuarcy =0\n        val_running_loss = 0\n        with torch.no_grad():\n            for val_data,val_label in test_dataset:\n                val_data,val_label = val_data.to(device),val_label.to(device)\n                val_output = model(val_data)\n                #loss function used is CrossEntropyLoss \n                loss_val = LOSS(val_output,val_label)\n                #calacuate the running loss by multiplying loss value by batch size\n                val_running_loss+= loss_val.item() * val_data.size(0)\n#                 val_running_loss+= loss_val.item() \n                _,pred = torch.max(val_output.data,1)    \n                total += val_label.size(0)\n                correct += (pred == val_label).sum().item()\n            val_accuarcy = correct/total\n            #calcuate loss per epoch by dividing runing loss by number of items in validation set\n            val_loss = val_running_loss/len(val_set)\n            print(val_running_loss)\n            print(\"=====/val/=====\")\n            print(\"epoch {} val accuracy {}\".format(epoch+1,val_accuarcy))\n            print(\"epoch {} val loss {}\".format(epoch+1,val_loss))\n            writer.add_scalar(\"val accuracy\",val_accuarcy,epoch+1)\n            writer.add_scalar(\"val loss\",val_loss,epoch+1)\n#             wandb.log({\"val accuracy\":val_accuarcy,\"val loss\":val_loss},step = epoch+1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batchsize = 16\n=====/train/=====\nepoch 10 train accuracy 0.62644\nepoch 10 train loss 0.8090380088090897\n8183.406176567078\n=====/val/=====\nepoch 10 val accuracy 0.58624\nepoch 10 val loss 1.6366812353134155","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%load_ext tensorboard\n%tensorboard --logdir=runs","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"wandb.finish()","execution_count":null,"outputs":[]},{"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":4}