【暑期實習】計算機視覺崗問題整理-阿里

筆試部分

還是有一些難度的,沒刷過一段時間的題目還可能應付不了這一個小時兩道題的強度,記錄一下筆試內容。

第一題

Question

有一疊撲克牌,每張牌介於1和10之間

有四種出牌方法:

  1. 單出1張
  2. 出2張對子
  3. 出五張順子,如12345
  4. 出三連對子,如112233

給10個數,表示1-10每種牌有幾張,問最少要多少次能出完

分析

這個題目如果需要找特定的規則去判斷,顯然是可以的,但是條件有點多。所以就不考慮這種方式,乾脆根據題目中的幾種情形分別去回溯,然後對回溯的結果去比較,選擇最小的一種方式來打牌。

能出三個對子,顯然需要要求,i,i+1,i+2的數目都大於2。同理,要出順子也要滿足一定的條件,我們只需要對該條件進行分別進行判斷即可。

大致整理一下代碼的核心思路,從小往大出牌,研究每一個數字可以如何出完。如果不能打連對或者連子的時候,出牌的次數是一定的,如果可以打的時候,計算打連對最少需要出的次數,打連子最少出的次數,然後打對子最少需要出的次數,然後最終取最小,就是出完當前數字的最少次數,然後依次計算每個數字出完的最小次數。
  這裏我只給出核心代碼,也並沒有處理輸入輸出的情況。大家自行處理。

Answer

class Solution1:
    def leastTimes(self,nums,begin=0):
        res=sum(nums[begin:]) # 表示剩餘牌的總數
        if not res:return 0
        if nums[begin]>0:
            if begin+2<10 and nums[begin]>1 and nums[begin+1]>1 and nums[begin+2]>1:
                for i in range(begin,begin+3): # 打了連對,狀態改變
                    nums[i]-=2
                res=min(1+self.leastTimes(nums,begin),res)
                for i in range(begin,begin+3): # 回溯,恢復狀態
                    nums[i]+=2

            if begin+4<10 and nums[begin+1] and nums[begin+2] and nums[begin+3] and nums[begin+4]:
                for i in range(begin, begin + 5): # 打了連子,狀態改變
                    nums[i] -= 1
                res=min(1+self.leastTimes(nums,begin),res)
                for i in range(begin, begin + 5): # 回溯,恢復狀態
                    nums[i] += 1
            if nums[begin]>1:
                nums[begin] -= 2 # 出對子,因爲出對子之後,可能當前的牌還有剩餘,所以繼續從當前牌型去搜索。
                return min(1+self.leastTimes(nums,begin),res)

            else : # 只有一張,只能考慮單張
                return min(res,1+self.leastTimes(nums,begin+1))
        else: # 表示沒有當前牌型,直接打下一張牌
            return self.leastTimes(nums,begin+1)

第二題

Question

首先定義上升字符串,\(s[i] \ge s[i-1]\),比如aaa,abc是,acb不是
給n個上升字符串,選擇任意個拼起來,問能拼出來的最長上升字符串長度

分析

我們定義一個上升字符串爲\(S_{ij}\),表示該字符串的首字符是 \(i\) ,尾字符是 \(j\),則\(dp_j=max(dp_{j−1},dp_i+S_{ij}.size())\quad i≤j\)。解釋一下這個公式,表示的是以字符\(j\)結尾的上升字符串最長的長度是以字符\(j-1\)結尾的上升字符串的最長長度,和以字符\(i\)結尾加上字符串從\(S_{ij}\)的和\(S_{hh}\)長度更新 \(dp_{*h}\) 的結果,我們就需要把\(S_{hh}\)放在\(S_{*h}\)之後處理。

也就是​說,我們根據最後一個字符排序,如果最後一個字符相同,我們則需要把第一個字符越大的越往後邊放。而且排序的過程中,因爲最後一個字符只有26種情況,這裏我們可以考慮使用基數排序或者桶排序,排序我這裏直接使用的是內建的排序,如果大家需要優化可以採用其他的。具體優化細節不談。

Answer

str_list = []
for _ in range(int(input())):
    str_list.append(input().rstrip())
str_list = sorted(str_list, key=lambda x: (x[-1], x[0]))
dp = [0] * 27
for i in range(len(str_list)):
    for j in range(26, 0, -1):
        tmp = len(str_list[i])
        st = str_list[i][0]
        end = str_list[i][-1]
        if ord(end) - ord('a') + 1 > j:
            continue
        dp[j] = max(dp[j], dp[ord(st) - ord('a') + 1] + tmp)
print(dp[26])

第一輪面試

這個是個預面試,就完全沒有準備的接到了面試,除去簡歷和項目之外的一些知識點總結一下:

L1 loss與L2 loss的區別和聯繫

L1 Loss

使用L1損失函數也被叫做最小化絕對誤差(Least Abosulote Error)。這個名稱非常的形象。LAE就是最小化真實值:

\[D_{L1}=\sum_{i=1}^{n}|y_i-f(x_i)| \]

這裏的\(D_{L1}\)其實就是平均絕對誤差(MAE)

使用L1損失函數也就是\(\min D_{L1}\)

L2 Loss

使用L2損失函數也被叫做最小化平方誤差(Least Square Error)。LSE就是最小化真實值

\[D_{L2}=\sum_{i=1}^{n}(y_i-f(x_i))^2 \]

這裏的\(D_{L2}\)其實就是平均絕對誤差

使用的L2損失函數也就是\(\min D_{L2}\)

L1 L2
穩健性高 穩健性一般
沒有穩定的解 有穩定的解
可能會得到多組解 只有一個解

穩健性

​ L1損失函數穩健性強是它最大的優點。面對誤差較大的觀測,L1損失函數不容易受到它的影響。這是因爲L1損失函數增加的只是一個誤差,而L2損失函數增加的是誤差的平方。當誤差較大時,使用L2損失函數,我們需要更大程度的調整模型以適應這個觀測,所以L2損失函數沒有L1損失函數那麼穩定。
​ 那麼,當我們認爲模型中可能存在異常值時,使用L1損失函數可能會更好;但是,當我們需要把誤差較大的觀測也納入模型中時,使用L2損失函數更好一些。

解的穩定性

​ 首先,從求解效率上來說,L2損失函數處處可導,而L1損失函數在零點位置是不可導的,這就使得使用L2損失函數求解可以得到一個解析解,而L1損失函數則沒有;
​ 其次,當數據有一個微小的變化時,L1損失函數的變化更大,其解更加的不穩定。

Pytorch中DataLoader Dataset Sampler的作用和關係

簡單來說,Sampler定義的是數據讀取的方式,可以使隨機讀取,可以是順序讀取,而Dataset定義的是某一批次的數據,提供數據內容。而DataLoader則是利用Sampler以及Dataset對網絡提供數據的另外封裝的接口。

比如Sampler分爲:

  • SequentialSampler
  • RandomSampler
  • WeightedSampler
  • SubsetRandomSampler

代碼如下:

class Sampler(object):
    r"""Base class for all Samplers.
    Every Sampler subclass has to provide an :meth:`__iter__` method, providing a
    way to iterate over indices of dataset elements, and a :meth:`__len__` method
    that returns the length of the returned iterators.
    .. note:: The :meth:`__len__` method isn't strictly required by
              :class:`~torch.utils.data.DataLoader`, but is expected in any
              calculation involving the length of a :class:`~torch.utils.data.DataLoader`.
    """

    def __init__(self, data_source):
        pass

    def __iter__(self):
        raise NotImplementedError
        
    def __len__(self):
        return len(self.data_source)

而Dataset裏面的結構爲:

class Dataset(object):
    def __init__(self):
        ...
        
    def __getitem__(self, index):
        return ...
    
    def __len__(self):
        return ...

得到什麼樣的數據,數據返回什麼樣的長度等等。

比如DataLoader的結構爲:

class DataLoader(object): 
    ... 
     
    def __next__(self): 
        if self.num_workers == 0:   
            indices = next(self.sample_iter)  
            batch = self.collate_fn([self.dataset[i] for i in indices]) # this line 
            if self.pin_memory: 
                batch = _utils.pin_memory.pin_memory_batch(batch) 
            return batch

collate_fn的作用就是將一個batch的數據進行合併操作。默認的collate_fn是將img和label分別合併成imgs和labels,所以如果你的__getitem__方法只是返回 img, label,那麼你可以使用默認的collate_fn方法,但是如果你每次讀取的數據有img, box, label等等,那麼你就需要自定義collate_fn來將對應的數據合併成一個batch數據,這樣方便後續的訓練步驟。

具體可以查看參考文獻裏面的關係。

BatchNormalization的原理以及參數作用

因此我們引入了這個可學習重構參數\(\gamma,\beta\),讓我們的網絡可以學習恢復出原始網絡所要學習的特徵分佈。

這個有點複雜了,以後再補充,可以查看參考文獻的細節。

知識蒸餾相關內容

知識蒸餾,可以將一個網絡的知識轉移到另一個網絡,兩個網絡可以是同構或者異構。做法是先訓練一個teacher網絡,然後使用這個teacher網絡的輸出和數據的真實標籤去訓練student網絡。知識蒸餾,可以用來將網絡從大網絡轉化成一個小網絡,並保留接近於大網絡的性能;也可以將多個網絡的學到的知識轉移到一個網絡中,使得單個網絡的性能接近emsemble的結果。

公式如下:

\[q_i=\frac{exp(z_i/T)}{\sum_j exp(z_i/T)} \]

T參數是什麼?有什麼作用?**

T參數爲了對應蒸餾的概念,在論文中叫的是Temperature,也就是蒸餾的溫度。T越高對應的分佈概率越平緩,爲什麼要使得分佈概率變平緩?舉一個例子,假設你是每次都是進行負重登山,雖然過程很辛苦,但是當有一天你取下負重,正常的登山的時候,你就會變得非常輕鬆,可以比別人登得高登得遠。

同樣的,在這篇文章裏面的T就是這個負重包,我們知道對於一個複雜網絡來說往往能夠得到很好的分類效果,錯誤的概率比正確的概率會小很多很多,但是對於一個小網絡來說它是無法學成這個效果的。我們爲了去幫助小網絡進行學習,就在小網絡的softmax加一個T參數,加上這個T參數以後錯誤分類再經過softmax以後輸出會變大(softmax中指數函數的單增特性,這裏不做具體解釋),同樣的正確分類會變小。這就人爲的加大了訓練的難度,一旦將T重新設置爲1,分類結果會非常的接近於大網絡的分類效果。

soft target(“軟目標”)是什麼?

soft就是對應的帶有T的目標,是要儘量的接近於大網絡加入T後的分佈概率。

hard target(“硬目標”)是什麼?

hard就是正常網絡訓練的目標,是要儘量的完成正確的分類。

兩個目標函數究竟是什麼?

兩個目標函數也就是對應的上面的soft target和hard target。這個體現在Student Network會有兩個loss,分別對應上面兩個問題求得的交叉熵,作爲小網絡訓練的loss function。

具體蒸餾是如何訓練的?

Teacher: 對softmax(T=20)的輸出與原始label求loss。

Student:(1)對softmax(T=20)的輸出與Teacher的softmax(T=20)的輸出求loss1。(2)對softmax(T=1)的輸出與原始label求loss2。(3)loss = loss1+loss2

第二輪面試

也是突然打電話過來,突然的面試,又打了一個措手不及。感覺這面試體驗有點糟糕。

面試內容和一面類似,這裏就補充一下上面沒有出現過的一些問題。

可導、可微和連續

對於一元函數有,可微<=>可導=>連續=>可積

對於多元函數,不存在可導的概念,只有偏導數存在。函數在某處可微等價於在該處沿所有方向的方向導數存在,僅僅保證偏導數存在不一定可微,因此有:可微=>偏導數存在=>連續=>可積。

可導與連續的關係:可導必連續,連續不一定可導;

可微與連續的關係:可微與可導是一樣的;

可積與連續的關係:可積不一定連續,連續必定可積;

可導與可積的關係:可導一般可積,可積推不出一定可導;

BN 所在網絡位置

增加了 Batch Normalization 的 DNN,其訓練步驟如下:

  1. 增加 BN 結構:對 DNN 中每一個 Activation,在它們前面放置一個 BN Layer(Batch Normalization Layer)。相當於以前的將\(Wu+b\)輸入 Activation Function,現在將BN( \(Wu+b\))輸入 Activation Function。至於爲什麼是在 Activation Function 前放置,而非整個 Hidden Layer 前放置,我們下面會解釋
  2. 求解參數:利用 BP 求解 DNN 中的參數

可能是因爲我筆試做的太爛又加了兩道算法題,兩個算法題都是手撕代碼在線編程,沒有編譯器只有IDE,做的對不對全靠面試官:

翻轉字符串

比較簡單,就不寫了。因爲用的python你懂得。

數組內查找兩數之和

也還OK,注意邊界條件。

總結

阿里的面試整體很看重你的算法能力和基礎能力。可能也是大佬比較多。但是感覺面試體驗很差,面試官很着急,不是一個和你探討問題的過程而是一個快速找到你的認知上下限的過程,並且每次面試沒有預約直接打電話隨時隨地進行面試感覺有一些不禮貌。也不知道是阿里的整體面試風格還是面試官個人風格,暫且不論了。

參考

2020年3月20日阿里內推筆試題

阿里巴巴校園招聘在線筆試3.20場

L1和L2損失函數(L1 and L2 loss function)及python實現

一文弄懂Pytorch的DataLoader, DataSet, Sampler之間的關係

Batch Normalization原理與實戰

知識蒸餾(Knowledge Distillation)簡述(一)

知識蒸餾(Distillation)相關論文閱讀(1)——Distilling the Knowledge in a Neural Network(以及代碼復現)

CNN中batch normalization應該放在什麼位置?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章