分類: python

  • The Devil is in the Pose: Ambiguity-free 3D Rotation-invariant Learning via Pose-aware Convolution 論文導讀

     
    本文建議有點雲特徵的先驗知識者閱讀,

    我會盡量講得平易近人,

    但仍建議請先參考此篇,先了解基本點雲處理如何處理特徵。

    這篇論文的重點在於改變了底層特徵方法。

    論文中由這張圖來解釋:

    因為現行解決 Rotation Invariant 的方法都是乘上一個矩陣去收集數據,

    並沒有考慮點與點之間的結構關係(i.e.上圖笑臉)。

    但是我覺得這個例子呈述得很容易誤導,

    聽起來很像在解決高低解析度、上下採樣的問題,

    實際上在點雲裡面應該是解決類似 KNN 特徵的問題才對。

    像是那三個有黑色像素的點,間隔距離、彼此夾角之類的結構關係所產生的資訊。

    作者認為現在的 CNN 都沒有抓到結構關係 (Pose) 的精華

    (e.g. 點雲中點與點的區域關係 AKA 底層特徵),

    所以要提出解決方法稱為 PaRI 的新架構來解決問題

    (我猜是 Pose: Ambiguity-free 3D Rotation-invariant 的縮寫)。

    舊有的底層特徵方法:

    好,那要改進一定得知道改哪裡嘛?

    之前別人是怎麼做這種結構特徵的?

    答案是:Point Pair Feature(PPF)

    Point Pair Peature

    Pr 是參考點,想像成 KNN 的目標點;
    Pj 是 Pr 附近的鄰居點,有 K 個;

    ∂n_r 是相較於 r 的三維座標軸,用  gram-schmidt orthogonalization 算出來的,

    忘記的同學可以去複習一下 GSO

    所以經過 PPF 計算,我們會得到一組四個特徵如下圖:

    這樣會出個小問題,各位想想好的底層特徵應該要具備什麼特性?

    應該要具備獨特性嘛!
    因為這樣訓練起來才不會與其他特徵混淆。

    仔細觀察 αn 的方程式我們會發現,沒有一條能分辨在這個半徑為 d 的圓圈上,

    有若干個法向量相同的  Pj 的辦法。 

    (∂1_r, ∂1_j 分別代表各自的法向量,ModelNet40資料集會給定。)

    改善方法:

    在改善之前,我們先想想舊有方法遇到什麼問題?

    問題是缺少 Pr 與 Pj 間的獨特特徵,對吧?

    所以我們希望新的方法最好讓每個 Pj 對 Pr 有獨特性。

    Local Reference Frame(LRF) +   Augmented Point Pair Feature(APPF)

    就能解決這個問題。

    LRF


    Local Reference Frame


    左邊那個飛機圖,是為了說明使用 Global Feature 會有問題而存在的,

    有興趣可以深入論文研究。

    LRF 這裡我們要先建三軸,首先要拿到 e^2_r;

    e^2_r = Pr – mr 的座標向量,mr 是 Pj 們的重心。

    接著根據公式建三軸:


    APPF

    Augmented Point Pair Feature

    接著把 d 投影到 π_d 上。

    什麼?你說 π_d 是什麼?

    論文這裡沒有寫得很清楚,

    我也看了很久,最後是對照著官方程式碼看才看出來。

    其實 π_d 就是座標向量 e^2_r! 

    投影上去,然後用 arctan 取角度回來。

    不知道為什麼用 arctan 而不用 tan 的同學,這裡有兩個理由:

    1.對應域

    i.e. tan(45°) = 1 = tan(225°)

    但是 arctan 只會給你 45°

    2.極值

    tan(89.9°) ≈ 5729.58,但 tan(90.1°) ≈ -5729.58。 

    arctan(5729.58) ≈ 89.9°,但 arctan(-5729.58) ≈ -90.1°。

    以上。

    若有任何錯誤請聯絡我xD

  • 一行就寫完 For 迴圈 – Python 列表推導式

     

    近期因為要趕畢業,

    大量參考前人大佬的 CODE,出現一堆列表推導式,

    加上 co-worker 學弟有資策會背景,對接的時候他也寫列表推導式,

    所以要學怎麼寫列表推導式,並留個紀錄。

     先來看一個簡單的例子:

    [expr0 for i in iterable if expr1]

    這種列表推導式等效於:

    for i in iterable:
        if (expr1):
            expr0
    超棒
    想要進階拓展 N 層迴圈?
    [expr0 for i in iterable for j in iterable if expr1]

    沒問題,這種表達式等效於:

    for i in iterable:
        for j in iterable:
            if (expr1):
                expr0 

    N 層迴圈也沒問題!
    準備好面對真正的難題了嗎?
    query, key, value = [l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2).contiguous() 
    for l, x in zip(self.linears, (query, key, value))]

    哇靠這在寫三小?

    先讓我們簡化一下程式碼:
    把:

    l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2).contiguous()

    改成:

    l(x)

    既然 self.linears 是某種包好的函式,替換成:

    fs(x)
    所以程式被我們簡化成:
    query, key, value = [l(x) for l, x in zip(fs, (query, key, value))]
    讓資料從列表推導式跑過一次:
    def f(x):
        a = x[0]*x[0]
        b = x[1]*x[1]
        c = x[2]*x[2]
        return [a, b, c]

    query = [1, 1, 1]
    key   = [2, 2, 2]
    value = [3, 3, 3]
    fs    = [f, f, f]

    print("n")
    print("Origin data:n")
    [ print(i) for i in (query, key, value) ]
    print("n")

    query, key, value = [l(x) for l, x in zip(fs, (query, key, value))]


    print("Processed data:n")
    [ print(i) for i in (query, key, value) ]
    print("n")
    結果會等於:
    Origin data:
    
    [1, 1, 1]
    [2, 2, 2]
    [3, 3, 3]
    
    
    Processed data:
    
    [1, 1, 1]
    [4, 4, 4]
    [9, 9, 9]
    唯一要注意 zip() 的關係,所以要造 fs。

    好棒,現在你也看得懂機器學習大佬的列表推導式了!

  • TelegramBot :花十分鐘開發聊天機器人,多執行十小時的程式!(Python)

     

    身為一個工科的研究生,你是否也有以下的困擾:

    "總是搶不到實驗室機器"、"擔心這個禮拜的實驗做得不夠多"、"睡前忘記收數據,導致下的實驗比別人少?"

    沒問題,你遇到的情況我通通都有遇過!

    筆者本人是心血來潮就會從臺中出發去花東吃早餐的放浪少年(#。

    但是週六日還要下實驗,那該怎麼辦?

    近期又要面臨暑期工作壓力,實驗進度更是要顧,

    所以產出了這篇文章。

    這篇文章能幫助你什麼?

    在 Python 程式跑完的時候,發 Telegram 訊息通知你。

    OK,讓我們開始吧。

    首先先安裝一下本文所使用的套件:

    pip3 install telegram
    pip3 install python-telegram-bot

    移到你目前想提醒 Python 程式的目錄底下

    cd ~/your_Python_program

     

    建立 Telegram 機器人的主程式(或是從這裡下載我寫好的)

    touch TelegramBot.py

    寫檔,接著複製貼上就可以了。

    vim TelegramBot.py
    import os
    import sys
    import re
    import configparser
    import telegram.ext
    import requests
    import time
    
    
    import logging
    
    from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
    
    # Enable logging
    logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                        level=logging.INFO)
    
    logger = logging.getLogger(__name__)
    # Initial bot by Telegram access token
    
    config = configparser.ConfigParser()
    config.read('config.ini')
    
    
    def start(update, context):
        """Send a message when the command /start is issued."""
        update.message.reply_text('Hi!')
    
    def echo(update, context):
        """Echo the user message."""
        update.message.reply_text(update.message.text)
    
    def help(update, context):
        """Send a message when the command /help is issued."""
        update.message.reply_text('Help!')
    
    def getUID(update, context):
        update.message.reply_text(update['message']['chat']['id'])
        print(update['message']['chat']['id'])
    
    def Alert():
        TOKEN = config.get('Bot','ACCESS_TOKEN')
        UID = config.get('Bot','UID')
        updater = Updater(token=TOKEN, use_context=True)
        dp = updater.dispatcher
        text ='實驗跑完啦!快上來收數據!'
        dp.bot.send_message(chat_id=UID, text=text) # 發送訊息
    
    
    def error(update, context):
        """Log Errors caused by Updates."""
        logger.warning('Update "%s" caused error "%s"', update, context.error)
    
    def main():
        
        TOKEN = config.get('Bot','ACCESS_TOKEN')
        UID = config.get('Bot','UID')
        updater = Updater(token=TOKEN, use_context=True)
        dp = updater.dispatcher
        dp.add_handler(CommandHandler("start", start))
        dp.add_handler(CommandHandler("help", help))
        dp.add_handler(CommandHandler("Alert", Alert))
        dp.add_handler(CommandHandler("getUID", getUID))
        dp.add_handler(MessageHandler(Filters.text, echo))
        updater.start_polling()
        updater.idle()
    
    if __name__ == '__main__':
        main()
    
    (然後向 BotFather 申請自己的機器人並拿到 Token,這部分可以看別的大大寫的這篇。)
    在同目錄底下建立 config.ini

    touch config.ini
    寫 config.ini
    vim config.ini

    把 your_token 換成你剛剛拿到的 TOKEN,
    UID 待會再教你拿:

    [Bot]
    ACCESS_TOKEN = your_token
    UID = your_uid

     

    填完之後,好沒問題來!
    我們來拿 UID!
    把這支程式跑起來:

    python TelegramBot
    如果一切沒問題,你的終端機會顯示:
    apscheduler.scheduler – INFO – Scheduler started
    再開 Telegram 到你與這隻機器人的對話框,輸入:

    /getUID

    這樣在 Telegram 對話框 及 終端機應該都會把你的 UID 噴出來,

    把 UID 回填到 config.ini 裡面去。
    然後把這支程式關掉(Ctrl-C)
    在你想監聽的 Python 程式最上面加入:

    from TelegramBot import Alert
    程式執行完處加入:

    Alert()
    把機器人跑起來,把你想監聽的程式也跑起來。
    當程式執行完時便會出現:

    已解決!

  • 【論文筆記】Squeeze-and-Excitation Networks


    簡介:

    Squeeze-and-Excitation Networks(以下簡稱 SENet)是一種類似於 plugin 的概念,

    獲得對於 C 個特徵(frature)間的關係,進而提升整體模型的準確率。


    方法概要:


    Step1

    先拿一組 H’ x W’ x C’ 的 feature ,經過 Ftr 後 得到標準輸入 U 。


    Step2

    想像一下把 U 這顆像生吐司的東西整個壓縮,經過 Fsq 後 變成 1 x 1 x C 的濃縮生吐司資料。

    然後這裡做了 channel-wise 的運算,也就是分組 base on C 的概念。

    做了以後會得到 C 組的 1 x 1 資料


    channel-wise 的概念請點這篇


    Step3

    因為濃縮生吐司資料的尺寸是 1 x 1 x C ,所以可以想像成有 C 片小土司。

    為了減少計算量 + 得到每片吐司 裡面模型的互相關係,所以做 Fex。

    得到權重序列 (1 x 1 x C)


    其中 W1,W2 的維度變換是因為 Activation Function 的緣故。


    Step4

    把權重序列 乘上生吐司 U (標準輸入 feature),

    讓你的生吐司成為威力加強版!


    想像一下,如果拿去做二維邊緣偵測,經過這個 SENet 之後的輪廓理論上會加深。



    解釋計算方程式:


    Ftr():

    Ftr 就是在做捲積運算,算到想要到生吐司尺寸。


    Faq():



    Zc 就是上文所提到的濃縮生吐司資料,這邊在幹的事情很簡單。

    把 H x W 這個二維平面上的每一個點做平均得到一個純量,總共有 C 個純量。

    正經且學術上的講法是 Global Average Pooling


    Fex():


    Fex() 是本文核心重點,利用 Fex() 獲得 Channel feature map 間的關係。

    首先要說明 σ  δ 的特殊性關係(#

    σ = sigmoid ; δ = ReLU

    z 則是等效上面的 Zc


    從右邊看起 維度 C 的 濃縮生吐司資料 左乘 W1 出來的資料維度是 1 x 1 x C/r ,

    這筆資料做 Sigmoid 後 左乘 W2 又變回 1 x 1 x C 的資料。



    最後 ReLU 運算後,得到權重序列 S


    Fscale():



    核心概念概念就是把這串權重序列 S 分配給傻傻的並聯生吐司 U,

    這樣你的 生吐司 U 就會變成擁有 Channel feature 間關係 的威力加強版 tilde X。



    文章的 Reference 來自於這兩篇:

    (深度學習)SENet(Squeeze-and-Excitation Networks)

    SENet(Squeeze-and-Excitation Networks)算法笔记


    如果小弟我有寫錯,或是有更好的想法歡迎來信討論~

    信箱地址:wuyiulin@gmail.com






  • [Python] YAML.dump TypeError: ‘str’ object is not callable 解決辦法

    當出現 TypeError: ‘str’ object is not callable

    但你確定傳進來的變數的確是 str 時?

    別緊張,可能只是命令參數給錯了。

    像是下圖只包到檔案名稱,沒把參數 ‘w’ 用 () 包起來,就會出現這種錯誤。

       

            yaml.dump(‘654’n + ’65’open(‘test.yml’),‘w’)

    修改成

            yaml.dump(‘654’n + ’65’open(‘test.yml’,‘w’))

    就可以囉OAOAOAOAOA

    低級錯誤me,撞豆腐自殺。

  • 自己寫全景處理程式 之 在 python 下 把 opencv 裝起來

    前情提要:

    中興煙火王子開的影像處理課,要我們自己手寫全景拼接三張圖片
    苦於實驗室配發的電腦開 matlab 開不起來,只好用 opencv 做

    python 下裝  opencv 有兩種方法,第一種是下 pip 或 conda 指令;第二種是去載.whl

    不過我用這台下 pip 裝不起來,本人認為 conda 邪教,所以在這邊我們要去載.whl

    什麼是 .whl 檔?

    就是一個給 python 吃的壓縮包,有點 .gz 的感覺

    通常只要下 pip install XXX.whl 就能裝起來了
    (但是 pytorch、PCL 例外)

    網址:https://pypi.org/project/opencv-python/#files

    裝完了 你好棒棒噢

    好棒棒個屁_(:3 」∠ )_

    因為全景處理要用 SIFT 或是 SURF 演算法

    所以我們要裝另外一個 DLC

    opencv-contrib-python
    如果不裝,call SURF 與 SIFT fun.的時候就會出現:
    cv2.cv2' has no attribute 'xfeatures2d'
    所以請下 
    
    pip install opencv-contrib-python
    
    
    好 恭喜你完成了全景的第一步 σ`∀´)σ