Howard-Kan
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # H 專題討論 - Google Landmark > 歡迎大家提出任何想問的問題 > 6/24 討論連結 https://meet.google.com/ozw-ytwz-irv ### 1. Data挑選與歸檔 ```python= import pandas as pd train_csv = pd.read_csv('/kaggle/input/landmark-recognition-2020/train.csv') train_csv.head(10) # put .jpg into the file name def add_txt(fn): return fn+'.jpg' train_csv['id'] = train_csv['id'].apply(add_txt) # choose those labels with more than 200 images, and choose the first 200 images of each label # move every training files to the same folder %cd /kaggle/working if not os.path.exists('training'): os.mkdir('training') if not os.path.exists('validation'): os.mkdir('validation') if not os.path.exists('testing'): os.mkdir('testing') import shutil import random label_list = train_csv['landmark_id'].unique() cnt = 0 final_label_list = [] for label in list(label_list): file_list = list(train_csv['id'][train_csv['landmark_id']==label]) if len(file_list) >= 200: final_label_list.append(label) if not os.path.exists('/kaggle/working/training/'+str(label)): os.mkdir('/kaggle/working/training/'+str(label)) if not os.path.exists('/kaggle/working/validation/'+str(label)): os.mkdir('/kaggle/working/validation/'+str(label)) if not os.path.exists('/kaggle/working/testing/'+str(label)): os.mkdir('/kaggle/working/testing/'+str(label)) for file in file_list[:120]: # 120 files for training src = '/kaggle/input/landmark-recognition-2020/train/'+file[0]+'/'+file[1]+'/'+file[2]+'/'+file dst = '/kaggle/working/training/'+str(label)+'/'+file if not os.path.exists(dst): shutil.copyfile(src, dst) for file in file_list[120:160]: # 40 files for validation src = '/kaggle/input/landmark-recognition-2020/train/'+file[0]+'/'+file[1]+'/'+file[2]+'/'+file dst = '/kaggle/working/validation/'+str(label)+'/'+file if not os.path.exists(dst): shutil.copyfile(src, dst) for file in file_list[160:200]: # 40 files for testing src = '/kaggle/input/landmark-recognition-2020/train/'+file[0]+'/'+file[1]+'/'+file[2]+'/'+file dst = '/kaggle/working/testing/'+str(label)+'/'+file if not os.path.exists(dst): shutil.copyfile(src, dst) cnt += 1 if cnt == 100: # only need 100 labels break # 20,000 files in total ``` 如果想要下載訓練資料 [點我](https://reurl.cc/9rR46X) ```python= from shutil import make_archive make_archive('train_data', 'zip', '/kaggle/working') %cd /kaggle/working from IPython.display import FileLink FileLink(r'train_data.zip') ``` 載入資料 ```python= from keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_dir = '/kaggle/working/training' validation_dir = '/kaggle/working/validation' test_dir = '/kaggle/working/testing' train_generator = train_datagen.flow_from_directory( train_dir, target_size=(256, 256), batch_size = 32, class_mode='categorical', seed=42) validation_generator = test_datagen.flow_from_directory( validation_dir, target_size=(256, 256), batch_size = 32, class_mode='categorical', seed=42) test_generator = test_datagen.flow_from_directory( test_dir, target_size=(256, 256), batch_size = 1, class_mode='categorical', seed=42) ``` ## 許佳雯 <font color="carol">**0622更新**</font> - 拆分train / test / val - 正則化+Resize + Augmentation - 建立模型 EfficientNetB0+Pooling+Dropout - 30epoch跑完 `loss:0.7207 / accuracy:0.8307 ` `val_loss:0.7517 / val_accuracy:0.8087` - 評估模型 `loss: 0.7614/ accuracy: 0.8050` ### 1.建立&訓練模型 使用Transfer Learning+Pooling+Dropout ```python= # Model + Transfer Learning-EfficientNetB0 from tensorflow.keras.applications import EfficientNetB0 from tensorflow import keras as K from tensorflow.keras import layers efficientNet = EfficientNetB0( weights='imagenet', include_top=False, input_shape=(256,256,3) ) model = K.models.Sequential() model.add(efficientNet) model.add(layers.GlobalAveragePooling2D()) model.add(layers.Dropout(0.01)) # 篩掉1/100 model.add(layers.Dense(100, activation='softmax')) #100個類別輸出 Adam = K.optimizers.Adam(lr=0.00001) #改善重點 model.compile(optimizer= Adam, loss='categorical_crossentropy', metrics=['accuracy']) print(model.summary()) ``` ![](https://i.imgur.com/gVVlrUj.png) ```python= train_history = model.fit(train_generator, validation_data = validation_generator, epochs=30, verbose=2) ``` ![](https://i.imgur.com/OCeFDkR.jpg) ![](https://i.imgur.com/6YHDkfT.png) ### 2.評估準確度 ```python= scores = model.evaluate(test_generator) scores[1] ``` -loss : 0.7614 - accuracy : 0.8050 0.8050000071525574 ## 甘元昊 #### 1. [MobileNetV2(較輕量)](https://reurl.cc/MA4Q2p) - train : validation : testing = 6 : 2 : 2 - train_datagen 做變形旋轉等處力 - 模型: (MobileNetV2+GlobalAveragePooling2D) - 100 epochs `loss: 0.0068 / accuracy: 0.9982 ` `val_loss: 0.4093 / val_accuracy: 0.9133` - 評估模型 `loss: 0.4393 / accuracy: 0.9112 ` 對training做image augmentation ```python= train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) ``` 建構模型 (MobileNetV2 + Pooling) ```python= from tensorflow.keras.applications import MobileNetV2 from keras.layers import Dense, Dropout, MaxPooling2D, GlobalAveragePooling2D, Flatten, Conv2D, Input from keras.models import Sequential from keras import optimizers import tensorflow as tf conv_base = MobileNetV2(include_top=False, weights="imagenet", input_shape=(256, 256, 3) ) conv_base.trainable = True model = Sequential() model.add(conv_base) model.add(GlobalAveragePooling2D()) model.add(Dense(100, activation='softmax')) model.compile(optimizer=optimizers.RMSprop(lr=2e-5), loss = 'categorical_crossentropy', metrics=['accuracy']) model.summary() ``` ![](https://i.imgur.com/DHJhcob.png) 訓練結果 ![](https://i.imgur.com/eK8aRtd.png) ![](https://i.imgur.com/vIdJ4P4.png) 繪製ROC curve ```python= import matplotlib.pyplot as plt from sklearn.metrics import roc_curve, roc_auc_score auc=[] plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.legend() plt.title('ROC') for i in range(100): fpr, tpr, _ = roc_curve(y, scores, pos_label=i) plt.plot(fpr, tpr, label=i) y_0_1 = [] for j in y: if j != i: y_0_1.append(0) else: y_0_1.append(1) y_0_1 = np.array(y_0_1) auc_of_the_label = roc_auc_score(y_0_1, scores) auc.append(auc_of_the_label) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.legend() plt.title('ROC') plt.show() ``` ![](https://i.imgur.com/bW7PJvm.png) 最高的AUC score: 0.72 (label 26028) 最低的AUC score: 0.26 (label 14915) #### 2. [EfficientNetB0 (Dropout)](https://reurl.cc/qg0g60) ps 這個設計上有點瑕疵,不小心把dropout放在output後面 - 模型: (EfficientNetB0+GlobalAveragePooling2D+Dropout) - 100 epochs `loss: 1.6511 / accuracy: 0.8919 ` `val_loss: 1.0362 / val_accuracy: 0.7660 `` - 評估模型 `loss: 1.04 / accuracy: 0.76 ` 模型 ```python= conv_base = EfficientNetB0(include_top=False, weights="imagenet", input_shape=(256, 256, 3) ) conv_base.trainable = True model = Sequential() model.add(conv_base) > model.add(GlobalAveragePooling2D()) model.add(Dense(100, activation='softmax')) model.add(Dropout(0.1)) model.compile(optimizer=optimizers.RMSprop(lr=2e-5), loss = 'categorical_crossentropy', metrics=['accuracy']) model.summary() ``` ![](https://i.imgur.com/lgNhEab.png) ![](https://i.imgur.com/6ONXNJn.png) ## 鄭怡伶 我的還在跑...先上傳程式碼,就是老師講義的那段CNN ![](https://i.imgur.com/OKgA1Ti.png) ![](https://i.imgur.com/HzT4TKU.png) ![](https://i.imgur.com/RNvHsih.png) ![](https://i.imgur.com/SPBG7K3.png) 結果不太好 ![](https://i.imgur.com/ny4LO8P.png) ![](https://i.imgur.com/w3CFrYK.png) ![](https://i.imgur.com/vutVbMg.png) 第二次,調lr=0.02 ![](https://i.imgur.com/abdpUKD.png) ![](https://i.imgur.com/LML59IO.png) 更新:調kernel_size變(3,3),lr=0.0255,epoch=50 ![](https://i.imgur.com/UnX4pjg.png) ![](https://i.imgur.com/rTOK1Ub.png) ![](https://i.imgur.com/dP41mnA.png) ![](https://i.imgur.com/k2u3kBO.png) 頂多到這樣了 ## 蔡中瑋 NASNetMobile ![](https://i.imgur.com/7Iqd6Cv.jpg) ![](https://i.imgur.com/nx5P7B3.jpg) ```python= NASNetMobile = tf.keras.applications.NASNetMobile( weights="imagenet", include_top=False, input_shape=(224,224,3) ) model = K.models.Sequential() model.add(NASNetMobile) model.add(GlobalAveragePooling2D()) model.add(Dropout(0.2)) model.add(layers.Dense(100, activation='softmax')) Adam = K.optimizers.Adam(lr=3e-5) model.compile(Adam, loss='categorical_crossentropy', metrics=['accuracy']) print(model.summary()) ``` ![](https://i.imgur.com/fsbOP1H.png) ```python= train_history = model.fit(train_generator, validation_data = validation_generator, epochs=100, callbacks = callbacks, verbose=1) ``` ## 盧彥愷 老師老師!!!!!! 假如你看到這邊的話 下面有很大一串是我打的講稿範例 以及自己的講稿 很大一串看完會累死 所以我在這邊想請你 幫我點左側標題的 盧彥愷想請教的問題的標題 他就會直接把你的頁面 拉到我想問你的問題 再麻煩你了 非常感謝!! 以下是大略講稿(參考用) 本次地標特徵專題選擇的是2020年google地標識別,第三屆地標識別大賽 主題是標記圖像中著名(和不那麼著名)的地標 比賽的標準為針對圖片進行地標識別,就像比賽介紹中的敘述 你有沒有看過你的假期照片並問自己:我在中國參觀的那個寺廟叫什麼名字? 透過對圖片辨識,就能辨別出我們眼前圖像的地標 能幫助我們更快的去回憶我們過往的旅途 或是計畫未來嚮往的精彩刺激的冒險 如果我們認不出美好回憶以及憧憬想望的圖片是位於哪裡? 是在哪個地標?我們又如何沉浸或展開新的旅途 這樣的訴求便是我們團隊選擇這個專題令人雀躍的原因 本次我們各自對專題發起壯烈的挑戰 想好好試試這樣猛烈的焰火 就像巧手的工匠那般 能不能千錘百鍊出我們這為其1個多月 產業新尖兵-人工智慧暨深度學習人才培訓課程 驗證並提升我們培訓出來的質變 雖是非常艱鉅的各自挑戰 說真的,自己想法子搞出地標辨識系統 可謂摸黑打老鼠到處碰壁 不是鬧著玩的!! 每個學員的程度不同 有的有一定編寫程式基礎,甚至幾番進過IT產業 此次參與培訓 只是想擴增自己對AI系統的眼界 有的就真的像打入輪迴 重新投胎一樣 一字一字開始認識python這樣的無字天書 很多只能意會不能言傳的地方 需要自己不斷實際編打 才能深入其境的體會 程式語言的奧妙 這樣的艱難就好像 俗話說的:瘦死的駱駝比馬大 沒基礎的要怎麼與在IT產業打滾已久的學員相比呢? 難免會由衷的失落 可俗話又說:拔了毛的鳳凰還不如雞呢 誰又知道零經驗的程式菜鳥,不會上演偶像劇般的逆襲呢?('燦笑') 更別說我們雖是各自挑戰 卻又將每個人各自凝聚出來的絲線 捲起來,擰成一股繩 我們並不是單打獨鬥 各自挑戰卻又互相Carry 每次的分組討論 互相加深彼此對程式的見解 最後將各自的成果 匯聚像濁水溪般川流不息 便是我們現在演講的成果 不停歇的砥礪學習 然而我們此次的挑戰 就像遙望希瑪拉雅山一般 光是望著就不寒而慄 1580470個訓練圖片資料 81313個訓練特徵 我們要怎麼建構出可以辨識這麼多組的資料 又能辨識出地標 還能分常見與不常見? 這樣處理海量資料的辨識系統呢? 突然覺得有點癡心妄想,哈哈 但是會走入程式員的這條路 何嘗不是懷揣著不見棺材不掉淚的執著呢? 就像本次專題訴求一樣 誰說編寫程式不也是場愉快又驚悚的冒險?苦笑 也許這樣的歷程 會讓我們看瞥見高山後面那絕美的風景 那個我們原以為跨越不了的峰頂,聖母峰,玉山之類的 沒有嘗試又怎麼知道 峰頂後面是藏著怎樣如詩如畫 一眼入魂的絕世饗宴 想想就讓人興奮呢!"癡笑" 那麼我們現在開始 我們各自組員的成果分享 還煩請各位專員耐心聆聽 我們驚心動魄的歷險 組員:盧彥愷發表 各位專員好,我是H組組員盧彥愷 我此次選用CNN(convolutional neural network) 卷積神經網路模型 做為我辨識地標的系統 我概略的介紹 我是如何看待 此專題的資料型態 特徵提取 資料前處裡手法裡的 格式變換 正則化 以及如何辨識 提取特徵 將特徵熱編碼 one-hot編碼的手法 如何建立cnn模型 選用怎樣的失誤函數 優化函數 訓練模型過程的介紹 訓練成果圖像化辨別 等 我是怎麼看待並進行建模的整體流程 我先是觀察 2020年google地標識別大賽 給予的資料型態 全是地標的圖片 有趣的是 每個分類中的圖片 不盡相同 有風景,有建築 有人像 非常的有趣 有趣的讓人直打滾 哈哈 然後分類的依據是 train.csv資料集中的 id資料 也就是每個地標圖片檔名 圖片的資料夾也是依照這個ID碼的前3碼 做為一層一層迭代資料夾的名稱 就比如有個檔名前3碼為123 這個圖片就會存在於 train訓練資料中的 1資料夾裡的2資料夾裡的3資料夾中 就好像俄羅斯套娃一樣 一層一層展開的 這樣多層的資料夾會導致 資料讀取及辨識特徵的困難 然而在train.csv資料集中 另外又給我們 landmark_id資料 許多的ID圖片檔名 都是依據這個landmark_id分類排序 並且是一個landmark_id就同時有 無數筆id資料 所以我們可以用landmark_id做為 圖片辨識的特徵來重新分類我們所有的圖片資料夾 這樣在相同的landmark_id下 所有圖片就會都被歸類在這個landmark_id資料夾裡 可以大大的簡化我們特徵提取的難度 並且加增資料演算的效率 然後我又在圖片資料上 思索著怎麼平均的 分配好做後面的資料處理 於是我做了以下的資料抽樣 先對train.csv資料集中 抽取前100名 圖片最多的landmark_id 發現 哇 最多的分類甚至有6272張 第100名卻只有364張 這樣資料量的差距太大 順帶一提 最少的分類竟然只有2張 很好 這樣離散分布的資料群 訓練成果一定會格外有趣呢"苦笑" 根本糟透了 所以為了不讓悲劇發生 我對前100名的圖片 進行隨機抽樣 100張的圖片 以10000張的圖片 100組的特徵 做為我的訓練資料的依據 並且對做為特徵資料的landmark_id 我也是用隨機排序 不知道這樣能不能降低 影像辨識的重複性 至於為何選用CNN的原因在於 透過卷積層的N x N 的矩陣 就好像濾波器一樣 透過分配不同的權重 一禎一禎的 偵測圖像的形狀 藉由偵測出的形狀輪廓及特徵 就能針對下一張的圖像做 相似度的辨識 並且池化層 又能壓縮圖檔 保留重要的特徵部分 大大的降低 模型的負擔 然後 此資料集是按照 landmark_id做分類 每個檔名都有著landmark_id 並且擁有相同的landmark_id 分成同一個資料夾的話 就表示 再同一個特徵資料夾的圖片 有著很高的相似程度 就像是20120的資料夾 就幾乎是聖塞巴斯蒂安大都會大教堂的圖片 ![](https://i.imgur.com/9VrhxU8.png) google後 就知道是在玻利維亞的羅馬天主教科恰班巴總教區 另外還有647292的資料夾 也幾乎是維多利亞紀念堂 ![](https://i.imgur.com/M8FmFBq.png) 為在印度的加爾各答 這些...等等的 當然也有奇怪的 像是286412的資料夾 就全是戰車的圖片 ![](https://i.imgur.com/QVkKtHz.jpg) 這應該不是地標吧??哈哈 所以可以想像google工程師都滿幽默的 所以這樣的資料集 非常適合用再 CNN影像相似度辨識上 這也就是我為什麼選用 CNN模型做為我本次訓練模型的原因 那麼 現在開始進行我程式碼的介紹 ```print= import import numpy as np import pandas as pd import os ``` #導入進度條 ```python= from tqdm.autonotebook import tqdm tqdm.pandas() ``` 設輸入資料路徑 ```python= base_dir = '/kaggle/input/landmark-recognition-2020' ``` 讀取train.csv檔中前100筆的資料 ```python= train = pd.read_csv(os.path.join(base_dir,'train.csv')) train.head(100) ``` ![](https://i.imgur.com/z0tNW3o.png) 抓出最多影像檔的前100組特徵(landmark_id)列表 ```python= head100 = pd.DataFrame(train['landmark_id'].value_counts().head(100)).reset_index() print(head100) temp_data = head100 print(temp_data) ``` ![](https://i.imgur.com/qDwauZG.png) 替換'landmark_id','count'名稱 ```python= temp_data.columns=['landmark_id','count'] print(temp_data.columns) print(temp_data) ``` ![](https://i.imgur.com/96yxglE.png) 將train列表與temp_data列表進行核對,篩選出train列表有temp_data列表的部分整列由小到大排列下來 ```python= sample_train_data = train[train['landmark_id'].isin(temp_data['landmark_id'])].reset_index(drop = True) sample_train_data ``` ![](https://i.imgur.com/9GaUuuL.png) ```python= import shutil ``` 將sample_train_data中的landmark_id編號,以不重複的方式提取出來成立成列表 ```python= landmark_id = list(sample_train_data.landmark_id.unique()) ``` ![](https://i.imgur.com/etM8fQs.png) 將sample_train_data中landmark_id標號裡的前100筆的資料列表以隨機方式提取出來進sample_train中 (等於從100個landmark_id中抽取隨機100組資料) ```python= sample_train = pd.DataFrame(columns=['id', 'landmark_id']) for i in landmark_id: fliter = (sample_train_data["landmark_id"] == i) df_shuffled = sample_train_data[fliter].sample(frac=1).reset_index(drop=True) kk = df_shuffled.head(100) sample_train = sample_train.append(kk,ignore_index=True) ``` ![](https://i.imgur.com/DG3iaZr.png) 這整串的程式碼,主要是透過定義函數,以及目標路徑的提取 將資料夾依landmark_id命名分類建立至目標路徑中 透過sample_train列表與landmark_id列表比對 若sample_train中的landmark_id與landmark_id列表中的landmark_id相同 將landmark_id相同的列表資料提取出來 其中包含了id名稱(圖片檔名)資料以及landmark_id資料 sample_train中包含了id名稱(圖片檔名)資料與landmark_id資料 landmark_id列表只有landmark_id資料 將提取出來的資料列表,其中的id名稱(圖片檔名)資料部分 丟進函式中與目標路徑(分類資料夾與原始train資料夾都建在相同的路徑下)進行拼接 mode('train') + id名稱(圖片檔名) + 副檔名,與目標路徑拼接 得出依landmark_id分類的原始train圖片路徑 再將原始train圖片路徑的圖片透過另一個函式 複製至事先經過landmark_id建立分類好的資料夾中 來進行train資料中圖檔的分類。 ```python= #定義函式(創建分類資料夾函式),藉由landmark_id建立分類資料夾,若資料夾存在就刪除,不存在就建立。 def create_folder_structure(base_dir,landmark_id, mode='train'): """ :param output_dir: :param mode: :return: """ base_dir = base_dir + '/' + mode if os.path.exists(base_dir): shutil.rmtree(base_dir) os.makedirs(base_dir) for id_ in landmark_id: os.makedirs(base_dir + '/' + str(id_)) print(base_dir) #目標路徑 output_dir = '/kaggle/working/landmark-recognition-2020/classification' #執行create_folder_structure函式建立分類資料夾 train_dir = create_folder_structure(output_dir,landmark_id, mode='train') #定義函式(取得檔案路徑(來源)函式),透過輸入路徑 #以及輸入經過sample_train與landmark_id比對好的id名稱(圖片檔名) #拼接出依照landmark_id分類好的原始train資料夾中圖片的路徑 def get_file_path(input_dir,path_id,mode='train'): prefix = path_id[:3] path = input_dir + "/" + mode + "/" +"{0}/{1}/{2}/".format(prefix[0],prefix[1],prefix[2]) filename = path_id return path + filename + ".jpg" #定義函式(複製動作函式),執行複製檔案函式 #並按照迴圈依次從原始train圖片路徑 #複製圖片進已經分類好(經由landmark_id)的資料夾路徑中 def copy(dataframe,output_dir,id_,mode='train'): destination = output_dir + "/" + mode + "/" + str(id_) for index,row in dataframe.iterrows(): shutil.copy(row['file_path'],destination) print(output_dir) #定義函式(複製檔案函式),執行sample_train與landmark_id的比對 #建立按landmark_id分類好的sample_train列表 #執行get_file_path函式,將sample_train列表中的id名稱(圖片檔名)輸入進get_file_path函式中 #將透過輸入id名稱(圖片檔名)進get_file_path函式中得出的原始train圖片路徑 #加入進train列表中 #將train列表中的原始train圖片路徑,輸入並執行copy函式,來進行圖片的複製 def copy_files(input_dir,output_dir,dataframe,landmark_id): for id_ in tqdm(landmark_id): print('Landmark with id: {}'.format(id_)) train = dataframe[sample_train['landmark_id']==id_] train['file_path'] = train.apply(lambda x: get_file_path(input_dir,x['id'],mode = 'train'),axis =1) # Copy training files copy(train,output_dir,id_,mode = 'train') #執行copy_files函式來進行複製檔案的操作 copy_files(base_dir,output_dir,sample_train,landmark_id) ``` 使用glob.glob函式遍歷抓取依landmark_id分類好的圖片路徑 將路徑加入進sample_train_list下 可用在後面透過路徑開啟圖像進行格式變換 圖像增強效果的實驗 如下列程式碼所示 ```python= sample_train_list = glob.glob(os.path.join(output_dir,'train/*/*')) ``` ```python= from numpy import expand_dims from matplotlib import pyplot as plt from keras.preprocessing.image import load_img from keras.preprocessing.image import img_to_array from keras.preprocessing.image import ImageDataGenerator import matplotlib.pyplot as plt import matplotlib.image as img image = img.imread(sample_train_list[0]) plt.imshow(image) plt.show() ![](https://i.imgur.com/ES61wfr.png) import cv2 imagt = cv2.resize(img, (128, 128), interpolation=cv2.INTER_AREA) plt.imshow(imagt) plt.show() ![](https://i.imgur.com/v3bS09j.png) # 將影像轉成轉成 影像 array。簡單講就是浮點數組。 data = img_to_array(imagt) # 將轉成 array 的影像 變成 [影像 array] 即[ x , x , x , x]格式 samples = expand_dims(data, 0) # 宣告 ImageDataGenerator 並套用左右位移 200 pixel 函數 train_datagen = ImageDataGenerator(width_shift_range=[-50,50]) # 透過迴圈,將影像一張一張套用進圖像增強生成器中 it = train_datagen.flow(samples, batch_size=1)#抓取圖像路徑至圖像增強生成器 for i in range(9): plt.subplot(330 + 1 + i) # 修出一張 3*3 的圖,+1 +i 指定到第幾張 batch = it.next() me_image = batch[0].astype('uint8')#陣列必須轉成整數形式才能被圖片開啟器變識 plt.imshow(me_image) plt.show() ![](https://i.imgur.com/VpidZCo.png) ``` 鍵出抓取出的training images個數 ```python= print('Number of training images: {}'.format(len(sample_train_list))) ``` ![](https://i.imgur.com/PsqYrSe.png) 定義x_train訓練資料路徑 ```python= x_train_bir = '/kaggle/working/landmark-recognition-2020/classification' ``` ```python= import os import glob import numpy as np from keras.preprocessing.image import img_to_array, load_img from PIL import Image ``` ## 盧彥愷 資料前處理的索引 定義函式(讀取目錄),建立空陣列image_list1 透過glob.glob函式遍歷 經過join函式拼接好的圖片路徑 形成圖片路徑list 將圖片路徑list中的路徑藉由迴圈一個一個的加入進下個迴圈中 ```python= size = (128,128) image_list1 = [] def read_directory(directory_name): for folders in glob.glob(os.path.join(directory_name,'train/*')): print(folders) #使用os.listdir函式抓取圖片路徑list中所有的圖檔 #透過迴圈將圖檔一張一張讀取出來 #並將圖片轉換成預設好的(128,128)尺寸 #然後再一張一張經過img_to_array函式變成影像陣列(陣列佔的記憶體比較小) #將一張一張影像陣列加入進image_list1中 for filename in os.listdir(folders): img=load_img(os.path.join(folders,filename)) img=img.resize(size,Image.BILINEAR) if img is not None: x=img_to_array(img) image_list1.append(x) ``` 將x_train訓練資料路徑輸入進並執行read_directory函式 即可讀取所有要用來train的圖片資料(陣列格式) ```python= read_directory(x_train_bir) ``` 將加入image_list1陣列中的所有影像陣列 連同image_list1陣列都轉換成多維陣列 ```python= train_image_list = image_list1 train_image_array = np.array(train_image_list) print("train_image_array.shapy={}".format(train_image_array.shape)) ``` 打開看起來都一樣 train_image_list[0] ![](https://i.imgur.com/q13ul0H.png) ![](https://i.imgur.com/Kb9mTSb.png) train_image_array[0] ![](https://i.imgur.com/gTxi2CT.png) ![](https://i.imgur.com/wK9aPEf.png) 也都是浮點數 然後套用進np.shpe確認格式的時候卻 檢視失敗 ![](https://i.imgur.com/6nXdFVb.png) 檢視成功 ![](https://i.imgur.com/mvHbSRG.png) 所以即便是陣列加入進空陣列裡 資料的架構上還是屬於列表 這也是我為什麼會給image_list1取名為列表的用意 以免自己忘記 所以執行陣列加入進空陣列的動作後 都要記得再次將陣列再次多維陣列化 將訓練陣列資料(影像陣列)轉成(train_image_array.shape[0]=影像筆數,128,128,3=顏色通道(1=單色,3=RGB))格式 嚴格上陣列資料就被轉成(x, x, x, x, )四個通道的陣列格式了 astype('float32')再小心謹慎的再次將資料轉成浮點數 一般來說當影像資料變成陣列後,就會從整數(電腦才能辨識成影像)變為浮點數了 然而這邊為求小心謹慎再次轉成浮點數 ```python= x_train4D = train_image_array.reshape(train_image_array.shape[0],128,128,3).astype('float32') ``` ![](https://i.imgur.com/ki8Zs3N.png) 將影像資料(陣列)正則化,也就是縮小至[0到1]的區間 因為影像陣列中,每筆資料(像素)的形式都是介於0~255之間 所以統一除以255就能對資料進行正則化 ```python= x_Train4D_normalize = x_train4D / 255 ``` ![](https://i.imgur.com/tRKhdud.png) 透過熱編碼將特徵資料做預處理 特徵資料為sample_train列表中的landmark_id 先前已將前100大圖片特徵的temp_data資料list 經過隨機抽取裡面的100筆資料 將list資料縮減加入進sample_train資料list中 所以sample_train裡包含的landmark_id資料 也被縮減至100*100 = 10000筆按照100個landmark_id特徵分類的圖像特徵 經過熱編碼後10000筆的landmark_id數字資料 將轉化成00~99的數字資料 此時還是Dataframe型態 ```python= from sklearn.preprocessing import LabelEncoder labelencoder = LabelEncoder() y_train_LabelEncoder = sample_train y_train_LabelEncoder['landmark_id'] = labelencoder.fit_transform(y_train_LabelEncoder['landmark_id']) y_train_LabelEncoder ``` ![](https://i.imgur.com/6JkESP2.png) 經過list化後以及陣列化後才會變成多維陣列的格式 ```python= lable = list(y_train_LabelEncoder.landmark_id) print(lable) landmark_label = np.array(lable) print(landmark_label) ``` ![](https://i.imgur.com/HtpJb0o.png) 這時候我們再透過random.permutation函式 將經過熱編碼處理過後的資料隨機排列 ```python= landmark_label_random = np.random.permutation(landmark_label) y_train_label = landmark_label_random y_test_label = landmark_label_random print(y_train_label) print(y_test_label) ``` ![](https://i.imgur.com/zYZjVyc.png) 再將隨機排列後的資料經過np_utils.to_categoricalc函式one-hot編碼後 就會得到10000筆,將00~99的數字資料轉成[0, 1, 0, ....100個[0或1]的組合] 格式即從(10000,)變為(10000, 100)的格式 所以建立模型的時候,最後的通道要設為100個輸出 才剛好對得上特徵資料的格式(為[x, 100]的通道) ```python= from keras.utils import np_utils y_TrainOneHot = np_utils.to_categorical(y_train_label) ``` ![](https://i.imgur.com/AAN9pzx.png) 建立簡易cnn模型 ```python= from keras.models import Sequential from keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D from keras.layers import Dense, SpatialDropout2D, Dropout, Flatten from keras.optimizers import SGD,RMSprop,Adam ``` ```python= model = Sequential() model.add(Conv2D(filters=32, kernel_size=(4,4), strides=(1,1), input_shape=(128,128,3), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(filters=64, kernel_size=(4,4), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(SpatialDropout2D(0.25)) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(100,activation='softmax')) model.compile(optimizer='Adam', loss='categorical_crossentropy', metrics=['accuracy']) print(model.summary()) ``` ![](https://i.imgur.com/Gp2pyvq.png) 訓練模型 ```python= train_history = model.fit(x = x_Train4D_normalize, y = y_TrainOneHot,validation_split=0.2, shuffle=True, epochs=20, batch_size=200,verbose=1) ``` ![](https://i.imgur.com/O0gqF0c.png) ![](https://i.imgur.com/1LXZapb.png) ![](https://i.imgur.com/dNwVKJL.png) 建立繪圖程式 ```python= import matplotlib.pyplot as plt def show_train_history(train_acc,test_acc): plt.plot(train_history.history[train_acc]) plt.plot(train_history.history[test_acc]) plt.title('Train History') plt.ylabel('categorical_accuracy') plt.xlabel('Epoch') plt.legend(['train', 'test'], loc='upper left') plt.show() ``` 顯示準確度及驗證準確度圖形 ```python= show_train_history('accuracy','val_accuracy') ``` ![](https://i.imgur.com/Ncnv3Pe.png) 顯示失誤及優化函數圖形 ```python= show_train_history('loss','val_loss') ``` ![](https://i.imgur.com/0Lh09c1.png) 顯示測試準確率 ```python= scores = model.evaluate(x_Train4D_normalize , y_TrainOneHot) scores[1] ``` ![](https://i.imgur.com/URHbZLS.png) ## 盧彥愷想請教的問題 老師老師看這邊!!!! 我現在的麻煩是 我的資料處理應該是完全沒有問題 但是 很可悲的再建立模型上面 可能出了很多錯誤 導致我的圖形 長這種鬼樣 ![](https://i.imgur.com/Ncnv3Pe.png) ![](https://i.imgur.com/0Lh09c1.png) ![](https://i.imgur.com/URHbZLS.png) 準確率也是這麼不爭氣 所以想麻煩你幫我看看 我建模 以及訓練程式碼的編打上是不是哪裡有問題? 還請你協助給點改進上的方向 然後我的資料及特徵如以下介紹 對train.csv資料集(train.csv裡有id資料集(圖片名稱),landmark_id資料集(特徵ID)) 抽取前100名 圖片最多的landmark_id 最多的分類甚至有6272張 第100名卻只有364張 這樣資料量的差距太大 另外查了 最少的分類只有2張 所以 我對前100名的圖片 進行隨機抽樣 100張的圖片 以10000張的圖片 100組的特徵 做為我的訓練資料的依據 並且做為特徵資料的landmark_id我也是用隨機排序 不知道這樣能不能降低 影像辨識的重複性 以下是我建模的整個流程 再麻煩你了,感謝!! 要是問題可能在我資料前處理的部分 那就麻煩你 幫我點擊左側 盧彥愷 資料前處理的標題 再麻煩你了!!! 忘了提另一個悲劇的事 ![](https://i.imgur.com/cqqgZNA.png) 我再kaggle開了GPU 但記憶體不足 我只訓練一次他就要崩掉了 根本沒辦法進行第二次就又要重新運行程式 所以10000筆圖檔即便讀取成多維陣列 10000筆看來是極限了... 這樣該怎麼解決呢... 建立簡易cnn模型 ```python= from keras.models import Sequential from keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D from keras.layers import Dense, SpatialDropout2D, Dropout, Flatten from keras.optimizers import SGD,RMSprop,Adam ``` ```python= model = Sequential() model.add(Conv2D(filters=32, kernel_size=(4,4), strides=(1,1), input_shape=(128,128,3), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(filters=64, kernel_size=(4,4), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(SpatialDropout2D(0.25)) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(100,activation='softmax')) model.compile(optimizer='Adam', loss='categorical_crossentropy', metrics=['accuracy']) print(model.summary()) ``` ![](https://i.imgur.com/Gp2pyvq.png) 訓練模型 ```python= train_history = model.fit(x = x_Train4D_normalize, y = y_TrainOneHot,validation_split=0.2, shuffle=True, epochs=20, batch_size=200,verbose=1) ``` ![](https://i.imgur.com/O0gqF0c.png) ![](https://i.imgur.com/1LXZapb.png) ![](https://i.imgur.com/dNwVKJL.png) 建立繪圖程式 ```python= import matplotlib.pyplot as plt def show_train_history(train_acc,test_acc): plt.plot(train_history.history[train_acc]) plt.plot(train_history.history[test_acc]) plt.title('Train History') plt.ylabel('categorical_accuracy') plt.xlabel('Epoch') plt.legend(['train', 'test'], loc='upper left') plt.show() ``` 顯示準確度及驗證準確度圖形 ```python= show_train_history('accuracy','val_accuracy') ``` ![](https://i.imgur.com/Ncnv3Pe.png) 顯示失誤及優化函數圖形 ```python= show_train_history('loss','val_loss') ``` ![](https://i.imgur.com/0Lh09c1.png) 顯示測試準確率 ```python= scores = model.evaluate(x_Train4D_normalize , y_TrainOneHot) scores[1] ``` ![](https://i.imgur.com/URHbZLS.png) > Hi, 我是 Howie 好ㄉ,針對盧同學的情形你 train 不進去的點在於因為你們資料量龐大,你一次性的使用 list 去存放記憶體會不構,要向其他組員用 imagedagtaloader 才可以在需要用的時候分批讀取。 > 針對你 ACC 圖很怪的問題其實也很簡單,模型資料量大,你卻只用一個小小模型去訓練,就是這樣 underfit 的結果(訓練時準確度有起來,但是測試時.....永遠的 ㄎ一ㄤ),可以可慮網路加深一點,甚至是向其他組員一樣,使用 keras application 中架構好的網路XD 盧彥愷:好的,感謝老師,我有用imagedagtaloader進行訓練了,可是圖形還是有點奇怪 ![](https://i.imgur.com/mC9XKb2.png) ![](https://i.imgur.com/jt7W0hH.png) ![](https://i.imgur.com/g6HBSj7.png) ![](https://i.imgur.com/cw7I0YU.png) ![](https://i.imgur.com/Tq5TRQL.png) ![](https://i.imgur.com/TyU9qpt.png) ![](https://i.imgur.com/flD75dm.png) ![](https://i.imgur.com/QcBjnir.png) ![](https://i.imgur.com/ZHXOuA4.png) ![](https://i.imgur.com/3lEtSAm.png) ![](https://i.imgur.com/rRjOeFC.png) 麻煩老師幫忙看了!! ![](https://i.imgur.com/kUcmGvW.png) ![](https://i.imgur.com/9q8oZn7.png) ![](https://i.imgur.com/OX2MGMK.png) ![](https://i.imgur.com/EDLpWna.png) 6/23 10:48更新 將資料分成三個資料夾路徑 ```python= train_bir1 = '/kaggle/working/landmark-recognition-2020/classification/train' validation_bir1 = '/kaggle/working/landmark-recognition-2020/classification/validation' test_bir1 = '/kaggle/working/landmark-recognition-2020/classification/test' ``` ![](https://i.imgur.com/8NEDmtq.png) 圖像增強 ```python= train_datagen = ImageDataGenerator(rescale=1./255,dtype='float') validation_datagen = ImageDataGenerator(rescale=1./255,dtype='float') test_datagen = ImageDataGenerator(rescale=1./255,dtype='float') ``` 生成器(資料已是先按6:2:2分配) ```python= x_train_generator = train_datagen.flow_from_directory( train_bir1, target_size=(256, 256), class_mode = 'categorical', color_mode='rgb', batch_size = 32, shuffle=True, ) ![](https://i.imgur.com/c4X0WIp.png) x_validation_generator = validation_datagen.flow_from_directory( validation_bir1, target_size=(256, 256), class_mode = 'categorical', color_mode='rgb', batch_size = 32, shuffle=True, ) ![](https://i.imgur.com/smbMo6j.png) x_test_generator = test_datagen.flow_from_directory( test_bir1, target_size=(256, 256), class_mode = 'categorical', color_mode='rgb', batch_size = 32, shuffle=True, ) ![](https://i.imgur.com/Q5U7aCz.png) ``` ![](https://i.imgur.com/oqFeDEs.png) ```python= from keras.models import Sequential from keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D from keras.layers import Dense, SpatialDropout2D, Dropout, Flatten from keras.optimizers import SGD,RMSprop,Adam model = Sequential() model.add(Conv2D(filters=32, kernel_size=(4,4), strides=(1,1), input_shape=(256,256,3), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(Conv2D(filters=32, kernel_size=(4,4), strides=(1,1), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(SpatialDropout2D(0.25)) model.add(Conv2D(filters=64, kernel_size=(4,4), strides=(1,1), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(Conv2D(filters=64, kernel_size=(4,4), padding='valid', activation='relu', kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(SpatialDropout2D(0.25)) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(128, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(100,activation='softmax')) model.compile(optimizer = 'Adam', loss='categorical_crossentropy', metrics=['accuracy']) print(model.summary()) ``` ![](https://i.imgur.com/gSEDzvZ.png) ![](https://i.imgur.com/hAHU6CU.png) ```python= train_history=model.fit_generator(x_train_generator, validation_data = x_validation_generator, validation_steps = 125, steps_per_epoch = 375, epochs=100, verbose=1) ``` ![](https://i.imgur.com/ll9IAa1.png) ![](https://i.imgur.com/Fz5zbs2.png) ![](https://i.imgur.com/DFovMPZ.png) 6/23 18:42 ![Uploading file..._1ioi47i4a]() ![Uploading file..._gvnus12io]() ![Uploading file..._7auqgzcxd]() ## 上台發表與QA PS 記得在簡報上放上動畫換頁的提示詞 1. 專題的分工? - 組長為甘元昊,也負責資料前處理 - 每個人都有訓練自己的模型,分別是今天報告的五種模型 2. 專題的困難點? - Kaggle系統有額度(一週30小時GPU, 一次最多跑九小時),沒辦法長時間train模型 - ROC原本為二元分類,利用遞迴方式來解決複數類別分類 3. 為何不直接參加競賽? - 因為過期了XD - Kaggle系統跑全部圖片會當掉 (GPU不足,硬碟空間不足) 4. 有沒有嘗試其他 ... Net - 以keras提供的模型為主 5. 為什麼選這個題目 - 可以練習架構深度學習模型,也相較容易說明預測結果

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully