- 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有多個。
Diffusion Model 主要是兩個過程,先從目標分佈中擴散,得到噪聲分佈,是熵增的過程;
然後是從噪聲分佈中預測出目標分佈。訓練過程就是訓練好這個x,這樣就能在隨機生成(例如高斯分佈 )的噪聲中獲得想要的目標分佈。
擴散過程是p,逆擴散過程是q。漂移量是兩者之間的差。
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。
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:只保留正區間的值並且保證線性。還能減輕梯度消失問題。但一旦輸入為負就不會再啟用神經元,讓神經元死亡。