{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"%matplotlib inline\n%config InlineBackend.figure_format = 'retina'\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport os\nfrom collections import OrderedDict\nimport torch\nfrom torch import nn\nfrom torch import optim\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms, models","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"대부분의 pretrain된 모델들은 224x 224이미지를 넣는것을 요구한다. 또한 모델이 학습될 때 정규화를 사용해아한다.  \n각 색상 채널을 개별적으로 정규화되고, 평균은 `[0.485, 0.456, 0.406]` 이고 표준편차는 `[0.229, 0.224, 0.225]`이다."},{"metadata":{"trusted":true},"cell_type":"code","source":"# pretrain된 모델을 다운받는다\nimport torchvision.models as models\nmodel = models.resnet18(pretrained = False)\nmodel\n# gpu를 사용할 수 있으면 device를 바꾼다\ndevice = torch.device('cuda:0' if torch.cuda.is_available()else 'cpu')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"!unzip ../input/dogs-vs-cats/train.zip -d train\n!unzip ../input/dogs-vs-cats/test1.zip -d test\ntrain_dir = '../working/train/train'\ntest_dir = '../working/test/test1'\ntrain_files = os.listdir(train_dir)\ntest_files = os.listdir(test_dir)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader, ConcatDataset\nfrom PIL import Image\nimport torchvision\nclass CatDogDataset(Dataset):\n    def __init__(self, file_list, dir, mode = 'train', transform =None):\n        self.file_list = file_list\n        self.dir = dir\n        self.mode = mode\n        self.transform =transform\n        #self.mode가 train으로 되어있고, file_list의 첫번쨰 행이 dog로 되어있으면 label 컬럼에 1을, 아니면 0\n        if self.mode =='train':\n            if 'dog' in self.file_list[0]:\n                self.label = 1\n            else :\n                self.label = 0\n    # file_list 데이터의 크기를 받음\n    def __len__(self):\n        return len(self.file_list)\n    #\n    def __getitem__(self, idx):\n        img = Image.open(os.path.join(self.dir, self.file_list[idx])) \n        # 한개이상의 경로 요소를 이어붙임 ex) os.path.join('c:', foo) -> (c:foo)\n        if self.transform:\n            img = self.transform(img)\n        if self.mode =='train':\n            img = img.numpy()\n            return img.astype('float32'), self.label\n        else :\n            img = img.numpy()\n            return img.astype('float32'), self.file_list[idx]\n        \ndata_transform = transforms.Compose([\n    transforms.Resize(256), #PIL 이미지를 256으로 변경\n    transforms.ColorJitter(),# brightness, contrast, saturation를 랜덤하게 변경 \n    transforms.RandomCrop(224), # PIT이미지를 무작위 위치에서 crop, output크기는 224,224\n    transforms.RandomHorizontalFlip(), # 주어진 확률만큼 horizontally filp시킴\n    transforms.Resize(128),\n    transforms.ToTensor()\n])\n    \ncat_files = [tf for tf in train_files if 'cat' in tf]# (train_file의 각 행)tf에서 cat이 있으면 tf를 cat_files(리스트)에 추가\ndog_files = [tf for tf in train_files if 'dog' in tf]\n\n#data_transform에서 제시된 인자대로 cat_file의 자료들을 변형\ncats =CatDogDataset(cat_files, train_dir, transform = data_transform)\ndogs =CatDogDataset(dog_files, train_dir, transform = data_transform)\n#합침\ncatdogs = ConcatDataset([cats,dogs])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader = DataLoader(catdogs, batch_size = 64, shuffle=True, num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samples, labels = iter(dataloader).next()\nplt.figure(figsize=(16,24))\ngrid_imgs = torchvision.utils.make_grid(samples[:24])\nnp_grid_imgs = grid_imgs.numpy()\n# tensor에서 이미지는 (batch, width, height),이다. 따라서 이를 보이기 위해서 이미지를 (w,h,b)로 전치시켜야한다.\nplt.imshow(np.transpose(np_grid_imgs,(1,2,0)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"개념 정리\n\n반복 가능한 데이터: iter() 함수로 반복자를 구할 수 있는 데이터\n* 반복자: next() 함수로 값을 하나씩 꺼낼 수 있는 데이터\n* iter() 함수: 반복 가능한 데이터를 입력받아 반복자를 반환하는 함수\n* next() 함수: 반복자를 입력받아 다음 출력값을 반환하는 함수"},{"metadata":{"trusted":true},"cell_type":"markdown","source":"## Classifier만을 위한 훈련"},{"metadata":{},"cell_type":"markdown","source":"어떤 모델은 훈련과 평가에서 batch normalization과 같은 다른 모듈을 사용한다.  \n이러한 모드를 바꾸기 위해서 `model.train()` 이나 `model.eval()`을 적절히 사용한다."},{"metadata":{"trusted":true},"cell_type":"code","source":"# parameter들을 고정\nfor param in model.parameters():\n    param.requires_grad = False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#resnet18의 위에 Classifier 아키텍터를 올림\nfc = nn.Sequential(OrderedDict([\n    ('fc1', nn.Linear(512,100)),\n    ('relu',nn.ReLU()),\n    ('fc2', nn.Linear(100,2)),\n    ('output',nn.LogSoftmax(dim=1))\n]))\n\nmodel.fc = fc","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 모델을 gpu로 변환\nmodel.to(device)\nmodel","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 모델을 훈련시키기 위한 function\ndef train(model, trainloader, criterion, optimizer, epochs = 5):\n    train_loss = []\n    for e in range(epochs):\n        running_loss =0\n        for images, labels in trainloader:\n            inputs, labels = images.to(device), labels.to(device)\n            \n            optimizer.zero_grad() # torch.Tensor에 있는 gradient를 정리함\n            img = model(inputs)\n            \n            loss = criterion(img, labels)\n            running_loss+=loss\n            loss.backward()\n            optimizer.step() # backward의 gradient를 출력해줌\n        print('Epoch : {}/{}..'.format(e+1,epochs),\n             'Training Loss : {:.6f}'.format(running_loss/len(trainloader)))\n        train_loss.append(running_loss) # running_loss들을 저장\n    plt.plot(train_loss,label = 'Training Loss')\n    plt.show()\n    \n\nepochs =3\nmodel.train() # train 모델들의 값\noptimizer = optim.Adam(model.fc.parameters(),lr=0.001)\ncriterion = nn.NLLLoss()\ntrain(model,dataloader, criterion, optimizer, epochs)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"NLLLoss  \n음의 log likelihood loss. C classes의 분류학습에 유용함  \n불균형 데이터셋에서 유용함  \ncf) CrossEntropyLoss = NLLLoss + Logsoftmax\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"#모델 저장\nfilename_pth = 'ckpt_resnet18_catdog.pth'\ntorch.save(model.state_dict(),filename_pth)\n\n# 테스트 데이터셋 변환\ntest_transform = transforms.Compose([\n    transforms.Resize((128,128)),\n    transforms.ToTensor()\n])\n\ntestset = CatDogDataset(test_files, test_dir, mode='test', transform =test_transform)\ntestloader = DataLoader(testset, batch_size = 64, shuffle=False, num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"# Testset에서의 예측"},{"metadata":{},"cell_type":"markdown","source":"기록을 추적하는 것(과 메모리를 사용하는 것)을 방지하기 위해, 코드 블럭을 `with torch.no_grad()`: 로 감쌀 수 있습니다. 이는 특히 변화도(gradient)는 필요없지만, requires_grad=True 가 설정되어 학습 가능한 매개변수를 갖는 모델을 평가(evaluate)할 때 유용합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\nfn_list = []\npred_list = []\nfor x, fn in testloader:\n    with torch.no_grad():\n        x = x.to(device)\n        output = model(x)\n        pred = torch.argmax(output, dim=1)\n        fn_list += [n[:-4] for n in fn] # fn\n        pred_list += [p.item() for p in pred]\n\nsubmission = pd.DataFrame({\"id\":fn_list, \"label\":pred_list})\nsubmission.to_csv('preds_resnet18.csv', index=False)        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samples, _ = iter(testloader).next()\nsamples = samples.to(device)\nfig = plt.figure(figsize=(24, 16))\nfig.tight_layout()\noutput = model(samples[:24])\npred = torch.argmax(output, dim=1)\npred = [p.item() for p in pred]\nad = {0:'cat', 1:'dog'}\nfor num, sample in enumerate(samples[:24]):\n    plt.subplot(4,6,num+1)\n    plt.title(ad[pred[num]])\n    plt.axis('off')\n    sample = sample.cpu().numpy()\n    plt.imshow(np.transpose(sample, (1,2,0)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# with 구문\n"},{"metadata":{"trusted":true},"cell_type":"markdown","source":"system resource 관리하는데 용이 (파일, locks)\nhttps://www.youtube.com/watch?v=iba-I4CrmyA"},{"metadata":{"trusted":true},"cell_type":"code","source":"# factor들을 줄일 수 있게 해줌\nwith open('hello.txt', 'w') as f: # f라는 이름으로 파일을 열고\n    f.write('hello, world!') # f에 문장을 입력하고 닫음\n    \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# with 없이 사용\nf = open('hello.txt','w')\ntry :\n    f.write('hello, world')\nfinally:\n    f.close()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Contest Manager\nclass ManagedFile:\n    def __init__(self,name): # 파일의 이름저장\n        self.name = name\n        \n    def __enter__(self):# enter이 불리면 열것임\n        self.file = open(self.name,'w')\n        return self.file\n    \n    def __exit__(self, exc_type, exc_val, exc_tb): #exc : 예외처리\n        if self.file:\n            self.file.close()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nwith ManagedFile('hello.txt') as f:\n    f.write('hello, world')\n\nmf = ManagedFile('hello.txt')\nmf","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with mf as the_file: #enter에 들어가서 결과값을 thefile로 받고\n    the_file.write('hello.txt') # the_file에 들어간 후 마지막에 __exit__ 에 있는 거승ㄹ 실행함","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from cntextlib import contextmanager\n@contextmanager\ndef managed_file(name):\n    try :\n        f = open(name,'w')\n        yield f\n    finally:\n        f.close()\n        \nwith manged_file('hello.txt') as f:\n    f.write('hello, world')\n    f.write('bye')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"","_uuid":"","trusted":true},"cell_type":"code","source":"import pandas as pd\nsampleSubmission = pd.read_csv(\"../input/dogs-vs-cats/sampleSubmission.csv\")","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}