{"cells":[{"metadata":{},"cell_type":"markdown","source":"forked from :https://www.kaggle.com/greatgamedota/xception-classifier-w-ffhq-training-lb-537"},{"metadata":{},"cell_type":"markdown","source":"필요한 모듈 임포트"},{"metadata":{},"cell_type":"markdown","source":"참조: https://blog.chemdev.net/27\n워닝모듈 사용하여 경고메시지 받지 않기"},{"metadata":{"id":"YjwlB710mIH_","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport cv2\nimport os\nfrom tqdm import tqdm,trange\nfrom sklearn.model_selection import train_test_split\nimport sklearn.metrics\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n#외부모듈 사용할 때, 경고메시지 출력 안함\nimport warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"PyTorch version:\", torch.__version__)\nprint(\"CUDA version:\", torch.version.cuda)\nprint(\"cuDNN version:\", torch.backends.cudnn.version())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gpu = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"id":"AaU3SeKMz_qm","colab_type":"text"},"cell_type":"markdown","source":"# Setup Data"},{"metadata":{"id":"K55cUb_0yTfH","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"# metadata0~46번까지 train_data 정보값 분류,47~49는 validation 정보로 분류하여 각각 데이터 프레임 만듦\ndf_train0 = pd.read_json('../input/deepfake/metadata0.json')\ndf_train1 = pd.read_json('../input/deepfake/metadata1.json')\ndf_train2 = pd.read_json('../input/deepfake/metadata2.json')\ndf_train3 = pd.read_json('../input/deepfake/metadata3.json')\ndf_train4 = pd.read_json('../input/deepfake/metadata4.json')\ndf_train5 = pd.read_json('../input/deepfake/metadata5.json')\ndf_train6 = pd.read_json('../input/deepfake/metadata6.json')\ndf_train7 = pd.read_json('../input/deepfake/metadata7.json')\ndf_train8 = pd.read_json('../input/deepfake/metadata8.json')\ndf_train9 = pd.read_json('../input/deepfake/metadata9.json')\ndf_train10 = pd.read_json('../input/deepfake/metadata10.json')\ndf_train11 = pd.read_json('../input/deepfake/metadata11.json')\ndf_train12 = pd.read_json('../input/deepfake/metadata12.json')\ndf_train13 = pd.read_json('../input/deepfake/metadata13.json')\ndf_train14 = pd.read_json('../input/deepfake/metadata14.json')\ndf_train15 = pd.read_json('../input/deepfake/metadata15.json')\ndf_train16 = pd.read_json('../input/deepfake/metadata16.json')\ndf_train17 = pd.read_json('../input/deepfake/metadata17.json')\ndf_train18 = pd.read_json('../input/deepfake/metadata18.json')\ndf_train19 = pd.read_json('../input/deepfake/metadata19.json')\ndf_train20 = pd.read_json('../input/deepfake/metadata20.json')\ndf_train21 = pd.read_json('../input/deepfake/metadata21.json')\ndf_train22 = pd.read_json('../input/deepfake/metadata22.json')\ndf_train23 = pd.read_json('../input/deepfake/metadata23.json')\ndf_train24 = pd.read_json('../input/deepfake/metadata24.json')\ndf_train25 = pd.read_json('../input/deepfake/metadata25.json')\ndf_train26 = pd.read_json('../input/deepfake/metadata26.json')\ndf_train27 = pd.read_json('../input/deepfake/metadata27.json')\ndf_train28 = pd.read_json('../input/deepfake/metadata28.json')\ndf_train29 = pd.read_json('../input/deepfake/metadata29.json')\ndf_train30 = pd.read_json('../input/deepfake/metadata30.json')\ndf_train31 = pd.read_json('../input/deepfake/metadata31.json')\ndf_train32 = pd.read_json('../input/deepfake/metadata32.json')\ndf_train33 = pd.read_json('../input/deepfake/metadata33.json')\ndf_train34 = pd.read_json('../input/deepfake/metadata34.json')\ndf_train35 = pd.read_json('../input/deepfake/metadata35.json')\ndf_train36 = pd.read_json('../input/deepfake/metadata36.json')\ndf_train37 = pd.read_json('../input/deepfake/metadata37.json')\ndf_train38 = pd.read_json('../input/deepfake/metadata38.json')\ndf_train39 = pd.read_json('../input/deepfake/metadata39.json')\ndf_train40 = pd.read_json('../input/deepfake/metadata40.json')\ndf_train41 = pd.read_json('../input/deepfake/metadata41.json')\ndf_train42 = pd.read_json('../input/deepfake/metadata42.json')\ndf_train43 = pd.read_json('../input/deepfake/metadata43.json')\ndf_train44 = pd.read_json('../input/deepfake/metadata44.json')\ndf_train45 = pd.read_json('../input/deepfake/metadata45.json')\ndf_train46 = pd.read_json('../input/deepfake/metadata46.json')\ndf_val1 = pd.read_json('../input/deepfake/metadata47.json')\ndf_val2 = pd.read_json('../input/deepfake/metadata48.json')\ndf_val3 = pd.read_json('../input/deepfake/metadata49.json')\n#위에서 분류한 값을 train 데이터 프레임은 df_trains로 묶고, val프레임은 df_val로 묶어서 저장.\ndf_trains = [df_train0 ,df_train1, df_train2, df_train3, df_train4,\n             df_train5, df_train6, df_train7, df_train8, df_train9,df_train10,\n            df_train11, df_train12, df_train13, df_train14, df_train15,df_train16, \n            df_train17, df_train18, df_train19, df_train20, df_train21, df_train22, \n            df_train23, df_train24, df_train25, df_train26, df_train27, df_train28, \n            df_train29, df_train30, df_train31, df_train32, df_train33, df_train34,\n            df_train34, df_train35, df_train36, df_train37, df_train38, df_train39,\n            df_train40, df_train41, df_train42, df_train43, df_train44, df_train45,\n            df_train46]\ndf_vals=[df_val1, df_val2, df_val3]\n#nums(0~49출력),LABELS(판별 결과값),val_nums(47~49로 지정)\nnums = list(range(len(df_trains)+1))\nLABELS = ['REAL','FAKE']\nval_nums=[47, 48, 49]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* 참조:파이썬 os 모듈관련(https://chess72.tistory.com/132)\n* 참조: 파이썬코딩도장 예외발생시키기( https://dojang.io/mod/page/view.php?id=2400 )\n* 참조: 파이썬 내장함수 zip 함수( https://wikidocs.net/32#zip)\n* 참조 : tqdm total 관련 (https://crazyj.tistory.com/167 )\n* 참조 : index 위치 변환 ( https://wikidocs.net/14#index )"},{"metadata":{"id":"FSPvZdzbzKd5","colab_type":"code","outputId":"fe78b0b0-aab7-4dfd-d33c-0af316b60f04","colab":{"base_uri":"https://localhost:8080/"},"trusted":true},"cell_type":"code","source":"#경로를 찾는 함수 선언,반환 값은 경로(path)\ndef get_path(num,x):\n    #입력받는 매개변수 num을 str(num)으로 문자열 값으로 변환하여 변수 num으로 저장한다.\n    num=str(num)\n    #문자열 변수 num의 길이가 2랑 같다면,즉 자릿수가 두자리라면((ex)12)\n    if len(num)==2:\n        #받아올 경로는path='../input/deepfake/DeepFake'+12+'/DeepFake'+12+'/' + x.replace('.mp4', '') + '.jpg'\n        #확장자(.mp4)를 공백으로 바꾸고 .jpg 더함.\n        path='../input/deepfake/DeepFake'+num+'/DeepFake'+num+'/' + x.replace('.mp4', '') + '.jpg'\n        # 그외, 여기서는 자릿수가 1자리 ex)9\n        #  path='../input/deepfake/DeepFake0'+9+'/DeepFake0'+9+'/' + x.replace('.mp4', '') + '.jpg'\n\n    else:\n        path='../input/deepfake/DeepFake0'+num+'/DeepFake0'+num+'/' + x.replace('.mp4', '') + '.jpg'\n    #위에서 받은 경로(path) 가 존재하지 않는다면\n    if not os.path.exists(path):\n        #예외(Exception)을 발생시킨다\n       raise Exception\n    return path\n\npaths=[]\ny=[]\n# for문 돌려서 df_trains와 nums를 묶어서(zip(df_trains,nums)) 해당되는 데이터프레임값과 nums의 인수를 받아 반복한다\n#tqdm문을 넣어서 total(반복개수)만큼 돌림.\nfor df_train,num in tqdm(zip(df_trains,nums),total=len(df_trains)):\n    #위에서 받은 데이터프레임(df_train)의 columns(열).values(값)=파일명,\n    #이 파일명을 list함수 써서 리스트로 만들어 images라는 변수에 저장한다\n    images = list(df_train.columns.values)\n    #위에서 만든 images라는 리스트에서 인수를 꺼내는 for문 시작한다\n    for x in images:\n        #정상적으로 작동된다면\n        try:\n            #for문 바깥에 선언한 paths라는 배열에, images에서 뽑아온 파일명으로,get_path(num,x)\n            #사용하여 경로를 구하여 추가한다\n            paths.append(get_path(num,x))\n            #df_train의 라벨값(FAKE OR REAL)을 LABELS에서 위치 변환('REAL'=0,'FAKE'=1)\n            #index함수로 위치변환 한 값을 y라는 배열에 추가한다\n            y.append(LABELS.index(df_train[x]['label']))\n        #예외가 발생하면 그냥 지나간다\n        except Exception as err:\n            #print(err)\n            pass\n#아래도 위의 train_df 처리과정과 같이 \n#val_paths,val_y 리스트에 값을 넣음.\nval_paths=[]\nval_y=[]\nfor df_val,num in tqdm(zip(df_vals,val_nums),total=len(df_vals)):\n    images = list(df_val.columns.values)\n    for x in images:\n        try:\n            val_paths.append(get_path(num,x))\n            val_y.append(LABELS.index(df_val[x]['label']))\n        except Exception as err:\n            #print(err)\n            pass","execution_count":null,"outputs":[]},{"metadata":{"id":"QXIIa5A-zfa3","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"#경로를 입력하면 해당 되는 이미지를 읽어오는 함수 선언\ndef read_img(path):\n    #openCV모듈 사용하여 이미지 읽은 다음,컬러변환 (cvtColor사용)하여 반환\n    return cv2.cvtColor(cv2.imread(path),cv2.COLOR_BGR2RGB)\n# 데이터를 넣으면 무작위로 섞어서 반환하는 함수 선언\ndef shuffle(X,y):\n    #빈 배열 선언\n    new_train=[]\n    #X,y 값에서 m,n 인수 값을 받아\n    for m,n in zip(X,y):\n        #위의 빈 배열에 [m,n]으로 추가\n        new_train.append([m,n])\n    #이렇게 형성된 new_train 배열을 무작위로 섞음    \n    random.shuffle(new_train)\n    #new_train=[[m,n],[m,n]...]\n    #X,y라는 빈 배열 선언\n    X,y=[],[]\n    #new_train에서 인수 x 값을 받아 반복문 실행\n    for x in new_train:\n        #X에 new_train배열의 첫번째 값 추가\n        X.append(x[0])\n        # y에 new_train 배열의 두번째 값을 추가\n        y.append(x[1])\n        #X,y값 반환\n    return X,y\n#랜덤함수 임포트\nimport random\n#매개변수로 train데이터 경로와 값,val이미지경로와val 값을 넣으면\n#해당되는 이미지 로드하고, 이미지와 y값을 무작위로 섞어서 반환하는 함수 선언\ndef get_random_sampling(paths, y, val_paths, val_y):\n  #real,fake라는 빈 배열 선언\n  real=[]\n  fake=[]\n  #train데이터의 경로 및 라벨 값 넣어서 m,n 인수 가져오는 for문 시작\n  for m,n in zip(paths,y):\n    #n(정답)이 0이면\n      if n==0:\n            #real에 경로추가\n          real.append(m)\n        #n값이 1이면 fake에 경로 추가\n      else:\n          fake.append(m)\n  # fake=random.sample(fake,len(real))\n\n  paths,y=[],[]\n    #real배열에서 인수 x 를 받아\n  for x in real:\n    #위의 paths배열에 해당 값 추가(경로명)\n      paths.append(x)\n        # y값 0추가(real)\n      y.append(0)\n    #fake배열도 똑같이 함\n  for x in fake:\n      paths.append(x)\n      y.append(1)\n#아래 validatin 값도 똑같이 진행해서 경로 값과 정답 리스트 구함\n  real=[]\n  fake=[]\n  for m,n in zip(val_paths,val_y):\n      if n==0:\n          real.append(m)\n      else:\n          fake.append(m)\n  # fake=random.sample(fake,len(real))\n  val_paths,val_y=[],[]\n  for x in real:\n      val_paths.append(x)\n      val_y.append(0)\n  for x in fake:\n      val_paths.append(x)\n      val_y.append(1)\n#X라는 빈 배열에 paths에서 img 인수 값 받아서\n#read_img(img)함수사용하여 img 반환하여 추가\n  X=[]\n  for img in tqdm(paths):\n      X.append(read_img(img))\n  val_X=[]\n  for img in tqdm(val_paths):\n      val_X.append(read_img(img))\n\n  # Balance with ffhq dataset\n#해당 경로의 폴더 안에 있는 파일 이름 가져오기\n  ffhq = os.listdir('../input/ffhq-face-data-set/thumbnails128x128')\n  X_ = []\n#ffhq 안에 있는 파일 불러와서,사이즈를 조절해서 im변수 저장\n  for file in tqdm(ffhq):\n    im = read_img(f'../input/ffhq-face-data-set/thumbnails128x128/{file}')\n    im = cv2.resize(im, (150,150))\n    #이렇게 저장한 im을 X_라는 배열에 저장\n    X_.append(im)\n  #for문 빠져나와서 배열 X_를 무작위로 섞는다  \n  random.shuffle(X_)\n\n  for i in range(64773 - 12130):\n    X.append(X_[i])\n    y.append(0)\n  del X_[0:64773 - 12130]\n  for i in range(6108 - 1258):\n    val_X.append(X_[i])\n    val_y.append(0)\n#X,y,val_x,val_y 값 무작위로 섞음\n  X, y = shuffle(X,y)\n  val_X, val_y = shuffle(val_X,val_y)\n\n  return X, val_X, y, val_y","execution_count":null,"outputs":[]},{"metadata":{"id":"HmvRDCqmaa_i","colab_type":"text"},"cell_type":"markdown","source":"# Dataset"},{"metadata":{},"cell_type":"markdown","source":"참조:데이터셋(https://wingnim.tistory.com/33)"},{"metadata":{"id":"7KNA5r-7afVp","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nmean = [0.485, 0.456, 0.406]\nstd = [0.229, 0.224, 0.225]\n#Dataset을 상속받은 class 선언\n#[이미지,라벨값]을 반환하는 class\nclass ImageDataset(Dataset):\n    def __init__(self, X, y, training=True, transform=None):\n        self.X = X\n        self.y = y\n        self.transform = transform\n        self.training = training\n\n    def __len__(self):#데이터 사이즈를 넘겨주는 파트\n        return len(self.X)\n    \n    def __getitem__(self, idx):#해당하는 인덱스에 해당하는 데이터 변환\n        if torch.is_tensor(idx):#해당하는 인덱스 값이 pytorch tensor이면 True 값 반환\n            #인덱스 값을 리스트로 만듦\n            idx = idx.tolist()\n        #X의 인덱스 값이 img로 저장(이미지)\n        img = self.X[idx]\n        #transform이 none이 아니면\n        if self.transform is not None:\n            #위의 img값을 transform(정규화등)에 res라는 변수에 저장\n          res = self.transform(image=img)\n          img = res['image']\n        \n        img = np.rollaxis(img, 2, 0)\n        # img = np.array(img).astype(np.float32) / 255.\n\n        labels = self.y[idx]\n        labels = np.array(labels).astype(np.float32)\n        return [img, labels]","execution_count":null,"outputs":[]},{"metadata":{"id":"-xvk_DhD1iUn","colab_type":"text"},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"markdown","source":"참조:pytorchcv.model_provider ( https://github.com/osmr/imgclsmob/blob/master/pytorch/pytorchcv/model_provider.py )"},{"metadata":{"id":"dcIJUiPx1DgP","colab_type":"code","outputId":"d8024ba7-ca65-4aed-c2c8-042a5274fdf4","colab":{"base_uri":"https://localhost:8080/","height":52},"trusted":true},"cell_type":"code","source":"#pytorchcv 라이브러리 설치\n!pip install pytorchcv --quiet\nfrom pytorchcv.model_provider import get_model\n#xception모델을 가져온다, 사전학습 한 걸로(pretrained=True) 가져옴\nmodel = get_model(\"xception\", pretrained=True)\n# model = get_model(\"resnet18\", pretrained=True)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* pretrained=True 하여 학습된 파라미터 값을 가져와서 모델 설정\n* 사전학습된 모델이라 output레이어가 1000개로 나옴,하지만 우리는 out_features를 1개로 설정해야 한다.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"model#다운받은 원본","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#모델의 해당 층에 있는 파라미터 출력=children\nlist(model.children())[:-1]#원래 모델에서 ouputlayer 빼고 나옴, 원래 모델은 1000개 분류,우리가 분류할 항목은 0아니면 1이니까,ouput_features=1로 \n#만들어야 함","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"참조: https://michigusa-nlp.tistory.com/26 (sequential모델 사용하기)"},{"metadata":{"trusted":true},"cell_type":"code","source":"#간단한 모델인 경우 Sequential에 (*모듈리스트)넣으면 리스트 안에 있는 내용이 들어감\nmodel = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Pooling(nn.Module):\n  def __init__(self):\n    super(Pooling, self).__init__()\n    \n    self.p1 = nn.AdaptiveAvgPool2d((1,1))\n    self.p2 = nn.AdaptiveMaxPool2d((1,1))\n\n  def forward(self, x):\n    x1 = self.p1(x)\n    x2 = self.p2(x)\n    return (x1+x2) * 0.5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model","execution_count":null,"outputs":[]},{"metadata":{"id":"jGr9EuSX1ZYI","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"#해당모델의 final_block의 pooling layer를 sequetial layer로 묶어 AdaptiveAvgPool2d(ouput_size=(1,1)로 변경한다\nmodel[0].final_block.pool = nn.Sequential(nn.AdaptiveAvgPool2d((1,1)))\n# model[0].final_pool = nn.Sequential(nn.AdaptiveAvgPool2d(1))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model","execution_count":null,"outputs":[]},{"metadata":{"id":"EEVBeVoW1cJX","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"#torch의 nn.Module을 상속하여 Head라는 클래스 선언\nclass Head(torch.nn.Module):\n    #__init__함수 선언하여 생성자 설정\n  def __init__(self, in_f, out_f):\n    \n    super(Head, self).__init__()\n    #생성자 변수 정의\n    self.f = nn.Flatten()\n    self.l = nn.Linear(in_f, 512)\n    #dropout 레이어 정의:앞층에 받는 산출물의 0.5 버림\n    self.d = nn.Dropout(0.5)#원래 0.75로 되어있는 걸 test데이터에 맞춤\n    self.o = nn.Linear(512, out_f)\n    self.b1 = nn.BatchNorm1d(in_f)\n    self.b2 = nn.BatchNorm1d(512)\n    self.r = nn.ReLU()\n  #클래스의 흐름을 위의 __init__함수 값 받아서 표시\n  def forward(self, x):\n    #Flatten(x)의 값 반환\n    x = self.f(x)\n    # nn.BatchNorm1d(입력 속성값개수):배치정규화레이어\n    x = self.b1(x)\n    #nn.Dropout(0.5):앞에서 받은 출력값의 0.5는 버림\n    x = self.d(x)\n    #nn.Linear(in_f, 512):선형함수\n    x = self.l(x)\n    #nn.ReLU():위의 선형함수 값을 Relu라는 활성화함수 통과시킴\n    x = self.r(x)\n    #nn.BatchNorm1d(512):512라는 특성 값을 받아 배치정규화\n    x = self.b2(x)\n    # nn.Dropout(0.5)\n    x = self.d(x)\n    #nn.Linear(512,출략되늩 특성 값)\n    out = self.o(x)\n    #이 클래스는 위의 out을 반환\n    return out","execution_count":null,"outputs":[]},{"metadata":{"id":"FRyOSBXy1wim","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"class FCN(torch.nn.Module):\n  def __init__(self, base, in_f):\n    super(FCN, self).__init__()\n    #앞에서 구조설계한 모델\n    self.base = base\n    #앞에서 정의한 Head class(입력특성,출력특성) out 값 반환\n    self.h1 = Head(in_f, 1)\n  \n  def forward(self, x):\n     #앞서 설계한 모델의 결과 x로 저장   \n    x = self.base(x)\n    #위의 head클래스의 반환 값을 return\n    return self.h1(x)\n#위에서 정의한 model,model의 출력 값이 2048이므로 이값을 입력값으로 받아\n#Head class의 out_feature 값을 반환\nmodel = FCN(model, 2048)\nmodel=model.to(gpu)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model","execution_count":null,"outputs":[]},{"metadata":{"id":"OPA6IyUJ1yxU","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"# !pip install torchtoolbox --quiet\n# from torchtoolbox.tools import summary\n\n# model.cuda()\n# summary(model, torch.rand((1, 3, 150, 150)).cuda())","execution_count":null,"outputs":[]},{"metadata":{"id":"pZv7D2KQ2YBk","colab_type":"text"},"cell_type":"markdown","source":"# Train Functions"},{"metadata":{},"cell_type":"markdown","source":"* 재현율 관련(recall):https://datascienceschool.net/view-notebook/731e0d2ef52c41c686ba53dcaf346f32/\n* np.clip함수: https://rfriend.tistory.com/426"},{"metadata":{"id":"Jc3QTjqj2XkJ","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"#예측값과 실제값의 오차를 구하는 함수 선언\ndef criterion1(pred1, targets):\n    #모델의결과값(pred1을 )sigmoid함수(이진분류활성화함수)를 통과한 값과\n    #실제값과의 오차를 binary_cross_entropy로 구함\n  l1 = F.binary_cross_entropy(F.sigmoid(pred1), targets)\n  return l1\n#모델을 학습시키는 함수 선언\ndef train_model(epoch, optimizer, scheduler=None, history=None):\n    #모델을 학습시켜야 하므로 train모드 설정\n    model.train()\n    #전체오차값을 0으로 초기화\n    total_loss = 0\n    #train_loder값을 tqdm으로 감싸서 학습과정 보자\n    t = tqdm(train_loader)\n    #train_loader에서 enumerate(펼쳐서)해당되는 인덱스와 파일을 매칭\n    for i, (img_batch, y_batch) in enumerate(t):\n        #데이터와 정답을 가져온다.\n        img_batch = img_batch.to(gpu).float()\n        y_batch = y_batch.to(gpu).float()\n        #optimizer 초기화한다:가중치 초기화\n        optimizer.zero_grad()\n        #model에 img_batch값을 넣어 결과 값을 받음\n        out = model(img_batch)\n        #오차값은 위에서 정의한 함수로 (모델의결과값,정답) 구함\n        loss = criterion1(out, y_batch)\n        \n        #총오차는 for문돌릴때마다 나오는 오차값을 누적\n        total_loss += loss\n        #tqdm 상태바 문구 설정,에포크/전체에포크 수, 학습률,평균오차 출력\n        t.set_description(f'Epoch {epoch+1}/{n_epochs}, LR: %6f, Loss: %.4f'%(optimizer.state_dict()['param_groups'][0]['lr'],total_loss/(i+1)))\n\n        if history is not None:\n          history.loc[epoch + i / len(X), 'train_loss'] = loss.data.cpu().numpy()\n          history.loc[epoch + i / len(X), 'lr'] = optimizer.state_dict()['param_groups'][0]['lr']\n         #역전파,비용함수 기준으로 역전파 한다\n        loss.backward()\n        #역전파 하는 동안 optimizer 사용하여 기울기등 parameter 최적화한다.\n        optimizer.step()\n        #스케줄러가 있다면\n        if scheduler is not None:\n            #스케줄러 통해, 학습률도 조정한다.\n          scheduler.step()\n\ndef evaluate_model(epoch, scheduler=None, history=None):\n    #모델평가므로 평가모드 설정\n    model.eval()\n    loss = 0\n    pred = []\n    real = []\n    #가중치 자동계산 안함(torch.no_grad):평가만 하므로\n    with torch.no_grad():\n        \n        for img_batch, y_batch in val_loader:\n            img_batch = img_batch.to(gpu).float()\n            y_batch = y_batch.to(gpu).float()\n            #for문 돌릴때마다 오차값 구해서 loss에 누적\n            o1 = model(img_batch)\n            l1 = criterion1(o1, y_batch)\n            loss += l1\n            #pred배열에 예측값 활성화 함수 통과한 값 더함\n            for j in o1:\n              pred.append(F.sigmoid(j))\n            #결과값의 인수 i값을 받아 real이라는 리스트 추가\n            for i in y_batch:\n              real.append(i.data.cpu())\n    #모델의 예측값을 활성화 함수 통과시키고 ,numpy배열로 변환한다\n    pred = [p.data.cpu().numpy() for p in pred]\n    pred2 = pred\n    #예측 값을 반올림\n    pred = [np.round(p) for p in pred]\n    #반올림 한 값을 넘파이 어레이 형태로 변환\n    pred = np.array(pred)\n    acc = sklearn.metrics.recall_score(real, pred, average='macro')\n    #real에서 인수를 뽑아내어 스칼라 값으로 반환\n    real = [r.item() for r in real]\n    #pred2를 넘파이 어레이 형태로 변환,clip 함수를 이용하여\n    #이 배열이 0.1~0.9 사이 값만 갖도록 변환\n    pred2 = np.array(pred2).clip(0.1, 0.9)\n    #로그로스 값을 구해서 kaggle 이라는 변수에 저장\n    kaggle = sklearn.metrics.log_loss(real, pred2)\n    #loss값의 평균을 구함\n    loss /= len(val_loader)\n    \n    if history is not None:\n        history.loc[epoch, 'dev_loss'] = loss.cpu().numpy()\n    \n    if scheduler is not None:\n      scheduler.step(loss)\n#결과값 출력함\n    print(f'Dev loss: %.4f, Acc: %.6f, Kaggle: %.6f'%(loss,acc,kaggle))\n    \n    return loss","execution_count":null,"outputs":[]},{"metadata":{"id":"XAhEFXSVKsyr","colab_type":"text"},"cell_type":"markdown","source":"# Dataloaders"},{"metadata":{"id":"n25nfarz8Gfi","colab_type":"code","outputId":"9961d38f-8461-4535-ef15-2a3d06ae64cd","colab":{"base_uri":"https://localhost:8080/","height":141},"trusted":true},"cell_type":"code","source":"#함수 사용하여 validation data 만듦\nX, val_X, y, val_y = get_random_sampling(paths, y, val_paths, val_y)\n#진짜 가짜 개수 출력\nprint('There are '+str(y.count(1))+' fake train samples')\nprint('There are '+str(y.count(0))+' real train samples')\nprint('There are '+str(val_y.count(1))+' fake val samples')\nprint('There are '+str(val_y.count(0))+' real val samples')","execution_count":null,"outputs":[]},{"metadata":{"id":"kfCLL0pt9Vh-","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"#데이타 셋 확장 및 정규화, 기타 편집 기능이 담긴 모듈 임포트\nimport albumentations\nfrom albumentations.augmentations.transforms import ShiftScaleRotate, HorizontalFlip, Normalize, RandomBrightnessContrast, MotionBlur, Blur, GaussNoise, JpegCompression\n#train데이터 변형함수 모아 놓음\ntrain_transform = albumentations.Compose([\n                                          ShiftScaleRotate(p=0.3, scale_limit=0.25, border_mode=1, rotate_limit=25),\n                                          HorizontalFlip(p=0.2),\n                                          RandomBrightnessContrast(p=0.3, brightness_limit=0.25, contrast_limit=0.5),\n                                          MotionBlur(p=.2),\n                                          GaussNoise(p=.2),\n                                          JpegCompression(p=.2, quality_lower=50),\n                                          Normalize()\n])\nval_transform = albumentations.Compose([\n                                          Normalize()\n])\n#데이타 셋 생성:앞에서 선언한 함수로 train,validatin set 생성한다.\ntrain_dataset = ImageDataset(X, y, transform=train_transform)\nval_dataset = ImageDataset(val_X, val_y, transform=val_transform)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이미지 시각화"},{"metadata":{"id":"P0Z_BWFJ-E5A","colab_type":"code","outputId":"db70092d-f2b0-4e17-fdfe-ffbb32bd9630","colab":{"base_uri":"https://localhost:8080/","height":499},"trusted":true},"cell_type":"code","source":"nrow, ncol = 5, 6\nfig, axes = plt.subplots(nrow, ncol, figsize=(20, 8))\n#차원을 축소함\naxes = axes.flatten()\nfor i, ax in enumerate(axes):\n    image, label = train_dataset[i]\n    image = np.rollaxis(image, 0, 3)\n    image = image*std + mean\n    image = np.clip(image, 0., 1.)\n    ax.imshow(image)\n    ax.set_title(f'label: {label}')","execution_count":null,"outputs":[]},{"metadata":{"id":"zbaUUqLIKwst","colab_type":"text"},"cell_type":"markdown","source":"# Train"},{"metadata":{},"cell_type":"markdown","source":"참조:스케쥴러 관련 ( https://teddylee777.github.io/deep-learning/keras-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98-vol-01 )"},{"metadata":{"id":"RJmdT2spBEU1","colab_type":"code","outputId":"bd51ed41-f0b2-49ec-8fa1-7c890243b78d","colab":{"base_uri":"https://localhost:8080/","height":405},"trusted":true},"cell_type":"code","source":"import gc\n\nhistory = pd.DataFrame()\nhistory2 = pd.DataFrame()\n\ntorch.cuda.empty_cache()\ngc.collect()\n\nbest = 1e10\nn_epochs = 60\nbatch_size = 32\n\n#트레인값을 가져오는 dataloader 생성한다,shuffle=True로 선언했으므로 진행할때마다 가져오는 이미지가 다르다\ntrain_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)\n#validation 이미지를 가져와야 하므로,shuffle=false로 설정해 진행할때마다 동일한 이미지 가져옴\nval_loader = DataLoader(dataset=val_dataset, batch_size=batch_size, shuffle=False, num_workers=0)\n\nmodel = model.to(gpu)\n#옵티마이저는 AdamW(Adam의 개선된 버전)\noptimizer = torch.optim.AdamW(model.parameters(), lr=0.001)\n#지역최저점에 빠질때 학습률을 줄여 빠져나오게 하는 스케줄러\nscheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, patience=5, mode='min', factor=0.7, verbose=True, min_lr=1e-5)\n\nfor epoch in range(n_epochs):\n    torch.cuda.empty_cache()\n    gc.collect()\n\n    train_model(epoch, optimizer, scheduler=None, history=history)\n    \n    loss = evaluate_model(epoch, scheduler=scheduler, history=history2)\n    #베스트 모델이 나오면 저장한다\n    if loss < best:\n      best = loss\n      print(f'Saving best model...')\n      torch.save(model.state_dict(), f'model.pth')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"LR: %6f, Loss: %.4f'%(optimizer.state_dict()['param_groups'][0]['lr'],total_loss/(i+1)))\n"},{"metadata":{"id":"vtSjo9DYtoEL","colab_type":"code","colab":{},"trusted":true},"cell_type":"code","source":"history2.plot()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## View this kernal for inference and submission: https://www.kaggle.com/greatgamedota/xception-binary-classifier-inference"}],"metadata":{"colab":{"name":"Deepfake_Detection.ipynb","provenance":[],"collapsed_sections":["AaU3SeKMz_qm","XsefoEdR1gHt"],"machine_shape":"hm"},"kernelspec":{"name":"python3","display_name":"Python 3"},"accelerator":"GPU"},"nbformat":4,"nbformat_minor":4}