AI/機器學習(計算機視覺/NLP)方向面試複習1

HazelXU發表於2024-07-30

目錄
  • 1. 判斷滿二叉樹
  • 2. 給定一個數,求該數的平方根,不用內建函式
  • 3. GAN model 內容
  • 4. Diffusion model 內容
  • 5. 二叉樹的建立,插入和刪除
  • 6. Linux相關命令:
  • 7. 快速排序
  • 8. xgboost和deepfm的效能。
  • 9. 判斷連結串列裡是否有環
  • 10. HDFS相關基礎知識
  • 11. Transformer具體流程
  • 12. aigc方向國內的典型研究機構以及代表性工作有哪些
  • 13. 兩數之和
  • 14. PaddlePaddle和PaddleOCR
  • 15. 列舉常見的機器學習演算法
  • 16. 如何處理使用者輸入特徵的稀疏資料?
  • 17. 重排連結串列
  • 18. 不同的啟用函式以及特點?

1. 判斷滿二叉樹

所有節點的度要麼為0,要麼為2,且所有的葉子節點都在最後一層。

#include <iostream>
using namespace std;
class TreeNode {
public:
	int val;
	TreeNode* left;
	TreeNode* right;
//建立的時候輸入引數x,會把x給val,nullptr給left和right 
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {};
	TreeNode(int x, TreeNode* l, TreeNode* r) : val(x), left(l), right(r) {};
};
bool isfull(TreeNode* cur) {
	if (cur == nullptr) return true;
	if (cur->left == nullptr && cur->right != nullptr || cur->left != nullptr && cur->right == nullptr) return false;
	if (cur->left != nullptr) {
		isfull(cur->left);
		isfull(cur->right);
	}
	else {
		return true;
	}

}

int main() {
	int x = 1;
	TreeNode* left = new TreeNode(1);
	TreeNode* right = new TreeNode(1);
	TreeNode* root = new TreeNode(1, left, nullptr);
	cout << isfull(root) << endl;

}

重點在於建立一個TreeNode類,並且寫出建構函式,呼叫建構函式建立節點。

2. 給定一個數,求該數的平方根,不用內建函式

二分法求解。遞迴。
``

float n;
float e = 0.001;
float findsquare(float left, float right) {
	float mid = (left + right) / 2;
	if (mid * mid - n >= 0 && mid * mid - n < e || mid * mid - n <= 0 && mid * mid - n >= -e) {
		return mid;
	}
	else {
		if (mid * mid > n) {
			findsquare(left, mid);
		}
		else {
			findsquare(mid, right);
		}
	}
}
int main() {
	cin >> n;
	cout<< findsquare(0, n)<<endl;

}

3. GAN model 內容

影像生成模型。影像生成模型比較瞭解的兩種是GAN和diffusion。

GAN的基本流程:生成器可以用任何輸出二維圖片的網路,例如DNN或者CNN。

Discriminator一般輸入為圖片,輸出為real或者fake。

每一輪,將reference輸入到discriminator裡判別為real,Generator輸出的輸入到discriminator裡判別為假。

Generator的損失函式和Discriminator的損失函式都是二元交叉熵,也就是評估真實資料的機率,Generator的目標是最大化二元交叉熵,也就是讓假結果都為正,而Discriminator是最小化二元交叉熵,讓假結果都為假。

4. Diffusion model 內容

首先是數學知識:

條件機率公式

基於馬爾科夫假設:當前機率僅與上一刻機率有關,與其他時刻無關。可以把條件機率其他項約掉。

高斯分佈的KL散度公式:

引數重整化:整理出z作為網路輸入,其他兩個作為網路引數,可求梯度的。

多元VAE目標函式,都是根據x推理出z,用z預測x。多元VAE的z有多個。

image

Diffusion Model 主要是兩個過程,先從目標分佈中擴散,得到噪聲分佈,是熵增的過程;

然後是從噪聲分佈中預測出目標分佈。訓練過程就是訓練好這個x,這樣就能在隨機生成(例如高斯分佈 )的噪聲中獲得想要的目標分佈。

擴散過程是p,逆擴散過程是q。漂移量是兩者之間的差。
image

5. 二叉樹的建立,插入和刪除

這裡應該是搜尋二叉樹,左節點小於自己,右節點大於自己。

刪除先不寫了不會

#include<iostream>
using namespace std;

class TreeNode {
public:
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
	TreeNode(int x, TreeNode* l, TreeNode* r) : val(x), left(l), right(r) {};
};
TreeNode* insert(TreeNode* cur, int x) {
	if (cur == nullptr) {
		return new TreeNode(x);
	}
	if (x < cur->val) {
		cur->left = insert(cur->left, x);
	}
	else if (x > cur->val) {
		cur->right = insert(cur->right, x);
	}
	return cur;

}
//有點複雜,先不寫了
TreeNode* deleteNode(TreeNode* cur, int val) {
	if (cur == nullptr) {
		return cur;
	}

}
int main() {
	int x = 1;
	TreeNode* root = new TreeNode(x);
	insert(root, 2);
}

6. Linux相關命令:

top 檢視程序資訊
df -h 檢視硬碟使用情況
ps aux 檢視所有程序
kill -9 pid 殺死編號為pid的程序
chmod 修改許可權
grep 從檔名中找到包含某個字串的資料
wc -l 統計行數
cut 分割一行內容
echo $PATH | cut -d ':' -f 3,5:輸出PATH用:分割後第3、5列資料
find -name 查詢檔案
vim 瀏覽 
head -3 顯示前三行內容
docker:
docker ps -a 檢視容器
docker attach 恢復容器
docker exec 掛起容器
docker run 跑容器
vim :n 到第n行 dd 刪除當前行 :q!直接退出 :wq儲存退出 gg=G格式化
ssh 登入伺服器 scp -r傳檔案

7. 快速排序

#include <bits/stdc++.h>
using namespace std;
const int N = 100001;
void quicksort(int * arr, int l, int r){
    if(l >= r) return;
    int i = l-1, j = r+1;
    int mid = (l+r) / 2;
    int x = arr[mid];
    while(i<j){
        do i++; while(arr[i] < x) ;
       do j--; while(arr[j] > x) ;
       if(i<j) swap(arr[i],arr[j]);
    }
    quicksort(arr, l,j);
    quicksort(arr, j+1, r);

}
int main(){
    int n;
    cin>>n;
    int arr[N];
    for(int i =0;i<n;i++){
        cin>>arr[i];
    }
    quicksort(arr,0,n-1);
    for(int i =0;i<n;i++){
        cout<<arr[i]<<" ";
    }cout<<endl;
}

總是會忘記的點:先do後while,i和j初始化為l-1和r+1,因為進入dowhile迴圈後會自增or自減。

quicksort(arr, l,j); 這裡不能用i代替j,因為i是一定大於x的,j是小於等於x的。要保證左邊的段是小於等於x,右邊的段是大於等於x。

8. xgboost和deepfm的效能。

(1)xgboost的結構:由多個迴歸決策樹的模型構成。每一步都加入一個新的樹。(前向分佈演算法,用貪心的策略)逐步最佳化基學習器。

最佳化第t棵樹時,前面t-1顆樹的引數是確定的。每輪的目標函式是n個樣本的最小損失+正則項

正則項是前t顆樹的複雜度。它由葉子結點的個數和每個節點值w的平方和決定,正則項是為了防止過擬合的。葉子節點越多,越容易過擬合。節點值大,就會導致這棵樹佔比比較多,也容易過擬合。

在機器學習中,一般透過梯度下降法最佳化引數。但是樹模型是階躍的,不連續的函式求不了梯度。所以xgboost是對每個葉節點求loss。 每個葉結點的loss可以用梯度來算,分別用了一階導數和二階導數也就是Hessian矩陣來找最優的分割點。

(2)xgboost如何用在推薦系統上?

將使用者的上下文資訊作為特徵輸入到xgboost中,預測使用者的點選機率。所以xgboost是做迴歸的,放入到裡面後

因為是迴歸任務,所以每輪迭代是選擇葉節點的分裂點,然後根據分裂點得到一個值,這個值就是點選機率。多個數就是加權求平均。樹的節點個數這些都是超引數。

(3)xgboost如何並行的?

並行時,在最優分裂點時用並行運算加快效率。它對特徵進行分塊,平行計算每個特徵的增益,透過增益找到最佳分割點。再同步結果,選擇最大的特徵進行分割。

xgboost相對於梯度提升樹(GBDT)有啥提升?

引入了二階導數(Hessian),這在最佳化過程中比傳統GBDT(只使用一階導數資訊)更為準確。

(4)Deepfm演算法:

deep factorization machines 因子分解機。它對低階特徵做特徵互動,另外一個DNN神經網路,做高階特徵互動。一般輸出是兩者的加權和。

因子分解機(FM)是什麼?

FM是SVM的擴充,更適合用於處理稀疏特徵。主要考慮到多維特徵之間的交叉關係(就像SVM的核函式,用內積,但是卻是用因子分解引數化的方式,而SVM中用的是稠密引數化的方式,這使得FM相比SVM的引數少了很多,更加容易計算)。其中引數的訓練使用的矩陣分解的方法。

例如對於電影評分中的資料,用onehot向量建模,一個特徵是非常稀疏的,非常長。因子分解機就是一種改進的二階多項式模型,考慮到兩個向量之間的相似性,例如喜歡這個型別電影的對另一個型別電影的喜歡。(推薦系統之FM(因子分解機)模型原理以及程式碼實踐 - 簡書)

本質上是用deepfm給召回階段的候選集合排序。所以做的僅僅只是排序,不是召回。Loss用的是adam。

(5)為什麼在大規模資料集上使用deepfm?

在處理使用者行為資料和隱式反饋資料時,DeepFM透過其深度部分能夠捕捉到複雜的非線性關係,表現較好。在大規模推薦系統中,如廣告推薦、商品推薦等,DeepFM具有優勢。

適合大規模資料和自動特徵學習的場景,尤其在處理高維稀疏特徵時表現出色。但需要大量資料和計算資源才能充分發揮其優勢。

9. 判斷連結串列裡是否有環

可以用雜湊法或者快慢指標法。快慢指標要注意:判斷fast的next。不然會出界,並且初始化兩個指標不能相同,不然當只有一個資料時返回就不對了。

    bool hasCycle(ListNode *head) {
        if (head == nullptr) return false;
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(slow != fast){
            if(fast == nullptr || fast->next == nullptr) return false;
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }

雜湊表法:注意插入是insert

bool hasCycle(ListNode *head) {
        unordered_set<ListNode*> sets;
        ListNode * cur = head;
        while(cur!=nullptr){
            if(sets.count(cur)) return true;
            sets.insert(cur);
            cur = cur->next;
        }
        return false;
    }

10. HDFS相關基礎知識

對hadoop瞭解的不多,主要是使用了一些hadoop的命令進行資料讀取。

HDFS是hadoop distribution file system。HDFS的檔案分佈在伺服器叢集上,提供副本和容錯率保證。

適用於儲存特別大的檔案,採用流式資料進行訪問。但不適合毫秒級別的訪問,是有點延時的。

我是使用了一些命令列的命令,例如:

hadoop fs -copyFromLocal // copy file
hadoop fs mkdir
hadoop fs -ls

11. Transformer具體流程

基礎的Transformer是隻用Attention,不用CNN or RNN。它相對於RNN最基礎的好處是可以並行。

multi-head指的是多輸出通道,self-attention指的是輸入的QKV是同一個東西。基礎的transformer是編解碼結構,編碼器和解碼器都包含n個transformer塊。

其中,Encoder裡的transformer塊裡的結構是:

輸入-》位置編碼-》multi-head attention->add和 norm->feed forward ->add和 norm ->輸出。其中,add就是殘差連線。

Decoder block裡多了一層Masked multi-head attention。

image

LayerNorm是對一個batch下所有樣本的各個維度分別執行標準化。例如,對某個batch所有樣本的第一維度做標準化。(因為輸入是一維,樣本拼在一起是二維,batch是第三維,所以在網路裡流通的是一個三維矩陣)

QKV是什麼? Query是輸入序列,key和value是attention裡的,將Query和key做內積得到Query和key的相似性,將相似性作為不同value的權重,輸出value的加權和就是注意力分數,再經過一個softmax計算注意力權重。

self-attention:指的是Q,K,V都來自同一個輸入,關注的是輸入內部的依賴關係。

Cross-Attention:Q 來自一個輸入(如解碼器),而 K 和 V 來自另一個輸入(如編碼器)。主要是用於多個序列作為輸入。例如機器翻譯,文字摘要,輸出的結果需要考慮到多個輸入序列。機器翻譯用編碼器-解碼器結構,就是解碼器關注到編碼器的輸出。

multi-head attention:QKV被分成多個頭,獨立進行運算,最後將結果拼接起來,提高模型的表達能力。

mask-head attention: 指的是多了一層掩碼,只看當前幀之前的序列。掩碼具體做法是在矩陣項城的時候,將那一個數置為-1e9,也就是負無窮。

12. aigc方向國內的典型研究機構以及代表性工作有哪些

AIGC AI-Generated Content 就是內容生成,包括GAN,VAE,stable diffusion(用於繪畫),GPT這些。

GPT:包括預訓練,fine tunning兩個階段,採用transformer的decoder作為特徵抽取器。預訓練是沒有人工參與的,用大量文字作為訓練材料。但是這樣給出的結果並不是想要的,這時就需要人工指導,來引導模型輸出想要的回答。這裡也算是增強學習的領域。在這方面還有以下的問題:

(1)糾正錯誤:透過Nerual editing 修改網路,讓網路不再輸入錯誤的結果

(2)保護隱私:Machine unlearing,讓網路遺忘一些學到的東西,以防洩露機密。

13. 兩數之和

    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> sets;
        for(int i =0;i<nums.size();i++){
            auto it = sets.find(target-nums[i]);
            if (it != sets.end()){
                return {it->second, i};
            }
            sets[nums[i]] = i;
        }
        return {};
    }

這題主要注意思路,用雜湊表找是否存在target-當前數。unordered_map查詢用find!=sets.end()

另外要考慮沒有的情況,返回{}
Q. 輸入的陣列特別長,以檔案的形式儲存的,存的是txt在磁碟上,需要一個t的儲存空間,是沒法直接載入到記憶體的。這樣的情況下怎麼辦

(1)用雜湊表來維護所有資料是否出現以及次數,這樣雜湊表最大的空間就是0-最大值。可以分塊處理,比如分成多個小塊,每塊小於記憶體容量,逐個讀取,重複的數不需要讀取。

(2)可以用c++的標準檔案流fstream讀取,每次讀取一行,將一行的資料用雜湊表儲存。

14. PaddlePaddle和PaddleOCR

在實習中,主要是用了paddleOCR建立工作流來監控訓練預測。工作流上是單獨的環境。每天是用shell指令碼來在伺服器上執行,定時啟動。

多個工作流啟動的模板:

function process1(){
    echo "1"
    sleep 10s
}
process1 > /work/log/1.log 2>&1 &
pid1 = 'echo $1'

echo 等待程序 $pid1
wait $pid1

15. 列舉常見的機器學習演算法

可以分成無監督和有監督的模型,有監督可以分成分類與迴歸。

常見的分類模型有SVM,決策樹,邏輯迴歸(用於二分類),隨機森林,貝葉斯分類器等等;

迴歸模型有線性迴歸,決策樹迴歸

無監督的演算法有k-means聚類,層次聚類,DBSCAN

16. 如何處理使用者輸入特徵的稀疏資料?

從資料裡獲取每天的觀看時長、點選數量、業務型別。先用特徵處理,對一些可以分類的型別(城市,片區,業務區,服務型別)進行onehot編碼。對於時間特徵(2022/2/18)這種做格式化,計算出觀看時長並用float記錄。其他項均為0.

17. 重排連結串列

這題主要三個步驟:先找中點,再反轉連結串列,再合併。三個地方我都有坑。

找中點簡單點,用快慢指標,注意判斷條件。

反轉連結串列主要是要初始化的問題,開始初始化時pre指向nullptr,cur指向當前。

merge簡單,但是在之前一定要記得把中間節點斷開!

class Solution {
public:
    ListNode * reverse(ListNode * head){
        ListNode* pre = nullptr, * cur = head, * nexth;
        while(cur != nullptr){
            nexth = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nexth;
        }
        return pre;
    }
    void merge(ListNode* node1, ListNode * node2){
        ListNode* n1next, *n2next;
        while(node1!=nullptr && node2 != nullptr){
            n1next = node1->next, n2next = node2->next;
            node1->next = node2;
            node1 = n1next;
            node2->next = node1;
            node2 = n2next;
        }
    }
    ListNode * middlenode(ListNode * head){
        ListNode* slow=head, *fast=head;
        while(fast->next != nullptr && fast->next->next != nullptr){
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
    void reorderList(ListNode* head) {
        if(head == nullptr) return;
        // 找中點
        ListNode* mid = middlenode(head);
        // 逆序
        ListNode * l1 = head;
        ListNode * l2 = mid->next;
        // 一定要斷開中間!
        mid->next = nullptr;
        l2 = reverse(l2);
        // 合併
        merge(l1,l2);

    }
};

18. 不同的啟用函式以及特點?

(1)softmax,attention用的啟用函式。能轉成一個平滑的分數並總和為1.

(2)relu:只保留正區間的值並且保證線性。還能減輕梯度消失問題。但一旦輸入為負就不會再啟用神經元,讓神經元死亡。

image

相關文章