據(jù)挖掘筆試復盤:考點、思路與備戰(zhàn)建議)
這段時間正值秋招高峰剛參加完貝殼找房2024年秋招機器學習/數(shù)據(jù)挖掘工程師的第二批筆試。整體做下來和第一批考完的同學交流了一下發(fā)現(xiàn)題型框架基本一致但在部分細節(jié)上明顯加深了。趁熱打鐵整理一份復盤筆記把題目還原、解題思路、踩坑點和后續(xù)準備建議都寫清楚給后面批次的同學一個參考。1. 貝殼這批筆試的整體畫像題量不大但處處是坑先說基本盤。整個筆試用時120分鐘通過牛客網(wǎng)在線作答沒有開攝像頭。題型分為四塊十幾道單選題、四五道多選題、兩道編程題、一道SQL分析題最后還有一道業(yè)務場景設計題。前兩部分覆蓋機器學習基礎、概率統(tǒng)計、特征工程和少量深度學習常識。整體看下來貝殼的筆試題風格屬于“基礎題為主、業(yè)務結合度高、編程題不算難但需要仔細讀題”的類型。印象最深的是客觀題部分很多知識點不是直接問概念而是給你一個具體場景讓你判斷該用什么方法或哪個指標。比如有一道題問正負樣本比例接近1:99時下面哪個評估指標最不適合用來衡量模型效果選項里有準確率、召回率、AUC、F1。這種題表面考指標實際考的是樣本不均衡場景下各評估指標的適用邊界。筆試時間分配我大致是客觀題40分鐘編程題35分鐘SQL題20分鐘業(yè)務題20分鐘最后留5分鐘檢查。實際做下來時間剛好夠沒有特別緊張但多選題比較磨人因為選錯、少選都不得分寧可少選也要保正確率。從內(nèi)容占比來看機器學習基礎與模型評估大約占了65%特征工程和數(shù)據(jù)預處理占了15%概率統(tǒng)計占了10%深度學習和工程向的內(nèi)容占了10%左右。這個分布也對應了貝殼業(yè)務對候選人的期待既要懂算法原理也要有數(shù)據(jù)敏感度和業(yè)務視角。2. 客觀題考點那些藏在基礎概念中的得分點與失分點2.1 模型評估與樣本不均衡場景化提問防不勝防貝殼的客觀題很喜歡把模型評估指標放在業(yè)務場景里考。除了上面提到的正負樣本比例1:99那個題還有一道多選是關于AUC和PR曲線的。題目大概是在一個用戶轉(zhuǎn)化率預測任務中正樣本占比非常低以下描述正確的是。這種題如果對PR曲線和ROC曲線的差異理解不透徹很容易選錯。關鍵知識點在于ROC曲線對樣本類別分布不敏感適合評估整體排序能力但在正樣本極度稀疏的情況下PR曲線能更直觀反映模型對正類的識別效果因為PR曲線的精確率和召回率都直接聚焦于正類。業(yè)務上如果更關注少數(shù)類比如轉(zhuǎn)化用戶、虛假房源PR曲線往往比ROC曲線更有參考價值。還有一個印象深刻的題是問在A/B測試中某策略組的轉(zhuǎn)化率比對照組高20%但p值為0.07以下哪個判斷是正確的這個題考的是假設檢驗和p值理解的邊界。很多人會直接選“該策略顯著優(yōu)于對照組”但實際上p值大于0.05意味著在95%置信水平下不能拒絕原假設結果并不顯著。選項里還有一個干擾項是“p值表示策略無效的概率”這個也是典型誤區(qū)p值不是H0為真的概率。2.2 特征工程連續(xù)特征離散化和缺失值處理的細節(jié)有一道題特別有意思問的是對連續(xù)特征做離散化后模型訓練效果提升最可能的原因是。備選答案包含了降低異常值干擾、增強特征魯棒性、引入非線性、減少過擬合等。做過實際項目的人都知道連續(xù)特征離散化本質(zhì)上是給模型引入了非線性映射能力同時對異常值不敏感。比如年齡特征直接輸入線性模型年齡和收入可能不是線性關系但分箱之后每個箱體對應一個獨立權重就能很好地捕捉這種非線性。這類題如果只背概念不做項目很容易漏選“引入非線性”這個關鍵點。缺失值處理的題也考了給出四個場景讓選合適的處理方式某特征缺失率達到60%但可能與目標強相關、某特征缺失完全隨機、某特征缺失與目標標簽相關等。核心邏輯是區(qū)分完全隨機缺失、隨機缺失和非隨機缺失。與標簽相關的缺失本身可能攜帶信息需要單獨編碼而不是簡單填充。2.3 GBDT與XGBoost的區(qū)別從原理層面理解而不是背結論多選題里有一道關于XGBoost相比GBDT的改進選項包括了支持二階泰勒展開、加入正則化項控制模型復雜度、支持列采樣、內(nèi)置缺失值處理。這題基本屬于送分題但容易漏選的是“支持列采樣”因為很多人只記住了XGBoost加了正則和二階導忽略了它對隨機森林列采樣的借鑒。我在復習時自己整理過一個對比表把GBDT、XGBoost、LightGBM的核心差異列出來考試時遇到這類題就很快維度GBDTXGBoostLightGBM目標函數(shù)一階導數(shù)二階泰勒展開二階泰勒展開正則化無顯式正則葉子節(jié)點數(shù)L2葉子節(jié)點數(shù)L2特征分裂預排序預排序近似直方圖基于梯度的單邊采樣互斥特征綁定缺失值處理無自動學習缺省方向自動學習缺省方向訓練速度較慢較慢快還有一道SVM相關的題給了一個非線性可分數(shù)據(jù)集的場景問選擇哪個核函數(shù)最合適。這個按經(jīng)驗選RBF核就行但要理解RBF核本質(zhì)上是在做特征映射到無窮維對非線性邊界擬合能力強不過對參數(shù)敏感核函數(shù)系數(shù)過大會過擬合。2.4 概率統(tǒng)計貝葉斯公式是每年必考的釘子戶貝葉斯公式幾乎是所有機器學習筆試必考的知識點。貝殼這批考了一道這樣的題某房源被用戶收藏的概率是8%其中真實優(yōu)質(zhì)房源占60%被收藏普通房源只有5%被收藏。若一個房源被收藏了問它是優(yōu)質(zhì)房源的概率是多少。這題需要設優(yōu)質(zhì)房源的先驗比例并套用貝葉斯公式計算后驗概率。我當時算的思路是設總體房源中優(yōu)質(zhì)房源比例為p則被收藏且為優(yōu)質(zhì)的概率是0.6p被收藏且為普通的概率是0.05(1-p)。根據(jù)全概率公式被收藏的總概率是0.6p 0.05(1-p)題目給出這個值等于8%先解出p再計算條件概率。這類題只要把公式記牢把變量的含義捋清楚一般不會出錯。2.5 正則化與過擬合L1和L2的區(qū)別要能從幾何角度解釋有一道多選題讓選出關于L1和L2正則化的正確說法。選項中涉及L1正則化更容易產(chǎn)生稀疏權重、L2正則化會讓權重更平滑、L1等價于拉普拉斯先驗、L2等價于高斯先驗。前三個都容易判斷第四個需要知道貝葉斯視角下正則化項對應參數(shù)先驗分布L1對應拉普拉斯先驗、L2對應高斯先驗。復習時還常被問到在高維稀疏特征場景下為什么L1比L2更常用。因為L1正則化會把不重要的特征權重壓縮到0天然做了特征選擇在高維場景下能顯著降低存儲和計算開銷。L2只是讓權重趨向于0但不會等于0稀疏性不夠。3. 編程題一個考特征工程直覺一個考排序評估邏輯3.1 第一題基于用戶行為日志構建活躍度特征題目大概意思是給定一個用戶行為日志表每一行是用戶對房源的一次行為行為類型包括瀏覽、收藏、約看、成交。權重分別是1、2、3、5?,F(xiàn)在需要實現(xiàn)一個函數(shù)輸入這個列表輸出每個用戶活躍度最高的Top10房源按照用戶維度分組并返回用戶ID、房源ID和累計活躍度得分。這道題本質(zhì)上考察的是分組聚合TopN排序。用Python寫的話最自然的做法是用defaultdict嵌套字典遍歷日志累加得分最后對每個用戶的房源字典按分數(shù)排序取前10。需要注意幾個細節(jié)同一用戶對同一房源可能有多條行為記錄需要累加如果不同房源得分相同按房源ID升序輸出避免結果順序不穩(wěn)定。我當時的參考實現(xiàn)思路如下from collections import defaultdict def top_houses(logs): user_house_score defaultdict(lambda: defaultdict(int)) weight_map {view: 1, favorite: 2, appoint: 3, deal: 5} for user_id, house_id, action in logs: user_house_score[user_id][house_id] weight_map[action] result [] for user_id, house_scores in user_house_score.items(): sorted_houses sorted(house_scores.items(), keylambda x: (-x[1], x[0])) for house_id, score in sorted_houses[:10]: result.append((user_id, house_id, score)) return result這道題不難但要注意時間復雜度和空間復雜度。如果日志量很大每個用戶的房源數(shù)量也很大全量排序后取Top10是可以接受的。但如果要在生產(chǎn)環(huán)境做更優(yōu)的做法是用heapq維護每個用戶一個大小為10的最小堆把排序復雜度從O(nlogn)降到O(nlog10)。筆試時間有限我直接用了排序方案思路清晰代碼量也少。對于類似場景如果是幾十萬用戶、幾千萬行為日志的規(guī)模用堆方案會穩(wěn)妥很多因為全量排序在內(nèi)存和時間上都會吃緊。3.2 第二題實現(xiàn)NDCGK評估指標第二題明顯比第一題更有區(qū)分度。題目設定是在搜索結果中真實相關程度用0到3分表示3分最相關。給定一個模型的排序結果房源ID列表和對應的真實相關分列表要求實現(xiàn)一個函數(shù)計算NDCGK。這題考察的是推薦/搜索排序場景下的標準評估指標實現(xiàn)非常貼近貝殼搜索排序的業(yè)務。如果之前沒有接觸過NDCG可能連公式都記不住。NDCG的全稱是Normalized Discounted Cumulative Gain核心思想是排序越靠前的相關文檔增益應該越大同時要做歸一化消除不同查詢下相關文檔總數(shù)不同的影響。計算流程分三步第一步計算DCGK。對每個位置i從1開始貢獻值是(2^reli - 1) / log2(i 1)把所有位置的貢獻值累加。第二步計算IDCGK。把真實相關分從大到小排序取前K個按照同樣的公式計算理想情況下的DCG。第三步NDCGK等于DCGK除以IDCGK。參考實現(xiàn)import math def ndcg_at_k(predicted_houses, relevance_scores, k): # 取前k個 pred_k predicted_houses[:k] rel_k relevance_scores[:k] dcg 0.0 for i, rel in enumerate(rel_k): dcg (2 ** rel - 1) / math.log2(i 2) # 理想排序按真實相關分降序 ideal_rel sorted(relevance_scores, reverseTrue)[:k] idcg 0.0 for i, rel in enumerate(ideal_rel): idcg (2 ** rel - 1) / math.log2(i 2) return dcg / idcg if idcg 0 else 0.0這道題有幾個容易出錯的地方。第一個是分母的位置偏移log2(i1)在Python代碼里要寫成math.log2(i2)因為索引從0開始。第二個是當IDCG為0時即前K個真實相關分全為0需要做保護直接返回0否則會出現(xiàn)除零異常。第三個是K大于列表長度時的邊界處理要明確是取全部還是補0題目里會說明如果沒說通常按取全部處理。還有一道小題回顧起來也值得說——要求寫一個函數(shù)對價格特征做z-score標準化。這個更簡單但需要注意標準差計算時要用總體標準差除以n而不是樣本標準差除以n-1因為在實際特征工程中對訓練集做標準化時用的是總體統(tǒng)計量。4. SQL分析題別急著寫代碼先把統(tǒng)計口徑定明白SQL題給的是一個簡化版的用戶行為數(shù)據(jù)表包含三張表用戶表、房源表、用戶行為日志表。題目要求統(tǒng)計2024年1月1日活躍用戶在1月8日7天后的留存情況并按城市維度輸出留存率。這類題在貝殼的業(yè)務場景里非常常見數(shù)據(jù)分析團隊經(jīng)常要統(tǒng)計各類留存指標。但筆試題的坑不在SQL語法本身而在于統(tǒng)計口徑。第一個口徑問題什么叫“1月1日活躍用戶”一般指當天有任意行為記錄的用戶需要去重同一用戶當天有多條行為只能算一次。第二個口徑問題“1月8日留存”如何判定通常是指這波用戶在1月8日當天仍有行為記錄。但這里有一個隱含分支——統(tǒng)計7日留存時到底是算第7天當天留存還是算1月2日到1月8日之間的活躍留存。不同業(yè)務定義不同。我在筆試時選擇了“第7天當天有活躍行為”這個口徑因為題目表述寫的是“1月8日7天后的留存情況”。第三個問題城市維度怎么關聯(lián)。行為日志表中沒有城市字段需要先關聯(lián)房源表拿到城市ID再關聯(lián)城市表拿到城市名稱。如果城市表里部分城市在1月1日沒有活躍用戶按內(nèi)連接還是左連接處理題目要求是“輸出所有城市的留存率”這里需要用左連接保留那些可能沒有活躍用戶的城市但要注意分母為0的情況。參考SQL大致長這樣with active_users as ( select distinct user_id from action_log where action_date 2024-01-01 ), retained_users as ( select distinct a.user_id from active_users a join action_log l on a.user_id l.user_id and l.action_date 2024-01-08 ), user_city as ( select l.user_id, h.city_id from ( select user_id, house_id, max(action_date) as last_action_date from action_log where action_date in (2024-01-01, 2024-01-08) group by user_id, house_id ) l join house h on l.house_id h.house_id ) select c.city_name, count(distinct uc.user_id) as active_user_cnt, count(distinct ru.user_id) as retained_user_cnt, count(distinct ru.user_id) / count(distinct uc.user_id) as retention_rate from user_city uc left join city c on uc.city_id c.city_id left join retained_users ru on uc.user_id ru.user_id group by c.city_name, c.city_id order by retention_rate desc;這道題我踩了一個小坑一開始直接用action_log和house表做join導致同一個用戶如果當天活躍多個房源會產(chǎn)生多條記錄后面用count(distinct)才兜住。如果當時直接count(1)計算用戶數(shù)結果會虛高不少。這種問題在真實業(yè)務中也經(jīng)常出現(xiàn)——先明確粒度再決定用什么聚合方式。考完之后復盤其實還有更簡潔的寫法。用窗口函數(shù)先給每個用戶標出1月1日是否活躍、1月8日是否活躍然后一次性聚合select c.city_name, count(distinct if(active_flag 1, u.user_id, null)) as active_users, count(distinct if(active_flag 1 and retain_flag 1, u.user_id, null)) as retained_users, count(distinct if(active_flag 1 and retain_flag 1, u.user_id, null)) / count(distinct if(active_flag 1, u.user_id, null)) as retention_rate from ( select user_id, max(if(action_date 2024-01-01, 1, 0)) as active_flag, max(if(action_date 2024-01-08, 1, 0)) as retain_flag from action_log where action_date in (2024-01-01, 2024-01-08) group by user_id ) u left join ... -- 關聯(lián)城市 group by c.city_name;這種方式在數(shù)據(jù)量大時性能更好邏輯也更集中。筆試時如果能寫出這種版本面試官觀感會更好。5. 業(yè)務設計題貝殼真正想考察的是落地思維業(yè)務題有兩道要求任選一道作答。我選的是“設計一個二手房估價模型并說明特征、模型選擇、評估指標和上線方案”。這道題相當貼近貝殼的主營業(yè)務也比較能體現(xiàn)一個算法工程師的綜合能力。回答這類題目我的框架是先定義目標和邊界再拆解數(shù)據(jù)與特征然后對比模型方案并給出評估方式最后說清楚落地和迭代。問題定義方面估價對象是掛牌二手房目標是預測房源的市場參考價而不是成交價。因為成交價受議價空間、買賣雙方心理因素影響比較大噪聲高。預測的粒度可以是房源級別也可以先預測小區(qū)均價再結合房源屬性做修正。實際業(yè)務中通常是兩者結合。特征工程部分我分了四層來回答第一層是房源靜態(tài)特征比如建筑面積、戶型、樓層、朝向、樓齡、裝修程度、是否有電梯。這些特征基本決定了房源的物理價值。第二層是小區(qū)特征包括小區(qū)均價、綠化率、容積率、物業(yè)費、建造年代。貝殼在這方面有天然的數(shù)據(jù)優(yōu)勢小區(qū)是核心組織單元。第三層是區(qū)位特征包括距離最近地鐵站的距離、周邊學校等級、商圈成熟度、醫(yī)院配套。這類特征對房價影響非常大但需要用POI數(shù)據(jù)和地圖數(shù)據(jù)加工。第四層是市場特征比如該小區(qū)近30天成交均價、掛牌量、去化周期。市場供需會直接影響價格預期。模型選擇方面表格類數(shù)據(jù)優(yōu)先考慮LightGBM或XGBoost。原因有幾個特征中既有數(shù)值型也有類別型樹模型對特征尺度不敏感可以自動處理缺失值和異常值訓練效率高迭代成本低可解釋性總體優(yōu)于深度模型。深度學習在這個場景并不是最優(yōu)選擇除非數(shù)據(jù)量特別大、特征之間關系非常復雜。評估指標方面不能只看MAE。我建議關注三個維度MAE衡量平均絕對誤差MAPE衡量相對誤差還要看定價誤差超過5%的樣本占比因為業(yè)務上如果偏差過大會影響用戶體驗。另外要按城市、小區(qū)、戶型維度做分層評估防止某些細分群體被系統(tǒng)性低估或高估。上線方案方面需要考慮模型更新的頻率。房價有明顯的時效性模型不能訓練一次用一年。比較穩(wěn)妥的方式是每周用最新成交數(shù)據(jù)做增量訓練每天用當天新增的掛牌和成交數(shù)據(jù)做特征更新。監(jiān)控方面要關注線上預測價格分布和實際成交價格分布的偏移比如連續(xù)一周預測價偏高就要進入排查流程。第二道業(yè)務題的選項是“設計一個小區(qū)的房源排序模型”。思路類似但重點換成了用戶行為特征用戶在搜索列表頁的點擊、詳情頁停留時長、收藏、約看、經(jīng)紀人咨詢等行為結合房源屬性和用戶畫像做個性化排序。評估指標自然就是均值NDCG或者TopK命中率。這類業(yè)務題沒有標準答案核心是看候選人能不能把問題拆解清楚有沒有真實項目經(jīng)驗。千萬不能只堆模型名詞比如一會兒用FM一會兒用DeepFM但說不清為什么選它。貝殼這類業(yè)務導向的公司更看重的是你對數(shù)據(jù)、業(yè)務、模型的綜合理解。6. 復盤思考哪些準備真正派上了用場6.1 機器學習基礎的系統(tǒng)復習是最劃算的投入這次筆試給我最大的感受是貝殼不管業(yè)務怎么復雜筆試階段還是老老實實考基礎。決策樹和信息增益的計算、SVM核函數(shù)的選擇、樸素貝葉斯的條件獨立性假設、集成學習的方差偏差分析這些經(jīng)典考點一個都沒少。如果你現(xiàn)在是在準備秋招的早期階段我的建議是不要急著刷一堆冷門論文或高階模型先把西瓜書和統(tǒng)計學習方法里的核心概念吃透做到能用自己的話解釋、能推導簡單公式、能結合場景判斷題目的對錯?;A題在筆試里占比高、得分確定性也高是性價比最高的復習項。我在考前兩周做了這樣一件事把機器學習的核心知識點按考點清單整理出來每個考點附上一道練習題每次復習時先合上筆記回憶一遍再對照檢查。這樣到考試時很多題目其實已經(jīng)見過了。6.2 編程題別只刷LeetCode業(yè)務場景題更重要LeetCode上的經(jīng)典算法題在筆試中不是重點。貝殼編程題的難度不高但背景都是業(yè)務相關的——行為日志處理、特征構建、排序評估。這種題目更考驗的是“用代碼解決實際數(shù)據(jù)問題”的能力。我當時復習方向就放在用Python熟練處理字典、集合、列表的嵌套操作熟悉排序函數(shù)的key參數(shù)用法掌握collections模塊里的defaultdict和Counter會用heapq做TopN。這些都是處理數(shù)據(jù)類編程題的基礎工具。如果能把pandas的基本操作也掌握寫這類代碼會更快但筆試環(huán)境不一定提供pandas所以原生Python的寫法必須要熟練。6.3 SQL的窗口函數(shù)和統(tǒng)計口徑是性價比很高的方向SQL這道題如果之前沒寫過窗口函數(shù)可能也有辦法通過子查詢和group by解決但會麻煩很多。建議集中練習一下row_number、rank、sum over、if結合聚合這類常見寫法。還有就是要養(yǎng)成先確認統(tǒng)計口徑再動筆的習慣——時間范圍怎么定、是否去重、連接方式選哪種、分母為0怎么處理這些想清楚再寫SQL往往能一步寫對。6.4 業(yè)務題提前了解公司業(yè)務能顯著提升回答質(zhì)量貝殼的業(yè)務核心是居住服務圍繞房產(chǎn)交易、租賃、家裝等場景。筆試前可以花一個小時看看貝殼App里有哪些核心功能思考每個功能背后需要什么樣的算法支持。比如搜索排序、房源估價、智能推薦、虛假房源識別這些都是很典型的算法落地場景。提前在心里過一遍流程現(xiàn)場遇到業(yè)務題時就不會慌。最后再說一個細節(jié)筆試時我習慣先把所有題目快速瀏覽一遍對每道題的難度和時間做個大致估算。比如這道SQL題比較費時間就標記一下先做后面的業(yè)務題最后再回頭處理。這種時間管理策略在這次筆試里幫了我不少因為客觀題里的多選題確實比預想中消耗時間。整體來看貝殼這批筆試的風格務實、貼近業(yè)務、不炫技認真準備過機器學習基礎的人會比較占優(yōu)勢。如果你的方向也是算法崗希望這份復盤能幫你少走一些彎路。