據(jù)分析可視化系統(tǒng)全鏈路實(shí)現(xiàn))
每年到了畢設(shè)季總有一批人看到“HadoopDjangoSpark租房數(shù)據(jù)分析可視化”這種題目就頭皮發(fā)麻。很多同學(xué)一方面覺(jué)得這個(gè)題目技術(shù)棧夠硬寫(xiě)進(jìn)簡(jiǎn)歷里不丟人另一方面又擔(dān)心三四個(gè)大數(shù)據(jù)組件疊在一起自己搞不定。這個(gè)擔(dān)心我特別理解因?yàn)檫@確實(shí)不是一個(gè)“套個(gè)模板就能跑”的題目但也不是什么高不可攀的東西。說(shuō)白了這套系統(tǒng)的核心就是一件很簡(jiǎn)單的事把一堆租房數(shù)據(jù)收集起來(lái)放進(jìn)一個(gè)能存大數(shù)據(jù)的倉(cāng)庫(kù)里用一套能快速計(jì)算的引擎去分析它再根據(jù)用戶(hù)的行為做推薦最后把結(jié)論用一個(gè)好看的頁(yè)面展示出來(lái)。數(shù)據(jù)流向是通的邏輯是順的難點(diǎn)不在單個(gè)技術(shù)而在怎么把這些東西串成一條線。這篇文章我會(huì)以實(shí)際的畢設(shè)項(xiàng)目為例把整套系統(tǒng)的設(shè)計(jì)思路、核心模塊拆分、關(guān)鍵代碼實(shí)現(xiàn)、環(huán)境聯(lián)調(diào)中的坑全部過(guò)一遍。不管你是剛拿到題目還在糾結(jié)怎么起步還是已經(jīng)寫(xiě)了一半卡在某個(gè)環(huán)節(jié)這篇文章都能讓你少走不少?gòu)澛贰?. 先看清這個(gè)項(xiàng)目的整體盤(pán)子1.1 題目拆解Hadoop、Django、Spark各自要干什么拿到題目第一件事不是急著寫(xiě)代碼而是先把題目拆開(kāi)。這個(gè)題目里有三個(gè)技術(shù)名詞它們并不是平等的而是各管一攤Hadoop負(fù)責(zé)“存”。原始數(shù)據(jù)量一大Excel和MySQL就撐不住了這時(shí)候需要HDFS來(lái)存儲(chǔ)海量房源數(shù)據(jù)。畢設(shè)的演示環(huán)境不要求多大但數(shù)據(jù)流向一定要走通。Spark負(fù)責(zé)“算”。Hadoop本身的計(jì)算模型偏重批處理寫(xiě)MapReduce又慢又啰嗦。Spark基于內(nèi)存計(jì)算處理結(jié)構(gòu)化數(shù)據(jù)快而且用Python寫(xiě)起來(lái)很順手適合統(tǒng)計(jì)區(qū)域均價(jià)、戶(hù)型分布、價(jià)格趨勢(shì)這類(lèi)聚合分析。Django負(fù)責(zé)“掛”。把分析結(jié)果和推薦結(jié)果通過(guò)Web頁(yè)面展示出來(lái)同時(shí)管理用戶(hù)登錄、收藏、瀏覽記錄這些行為數(shù)據(jù)是整個(gè)系統(tǒng)和用戶(hù)交互的門(mén)面。這套組合在邏輯上是自洽的數(shù)據(jù)量大就上Hadoop存計(jì)算復(fù)雜就上Spark算交互展示就交給Django。它既不是單純的大數(shù)據(jù)項(xiàng)目也不是普通的Web管理系統(tǒng)而是把“數(shù)據(jù)倉(cāng)庫(kù)計(jì)算引擎Web應(yīng)用”串成了一個(gè)完整的業(yè)務(wù)閉環(huán)。1.2 技術(shù)選型為什么這么定很多同學(xué)會(huì)問(wèn)為什么不直接用MySQLSpringBoot或者干脆用Pandas算完再用Flask展示這個(gè)問(wèn)題我在答辯的時(shí)候也反復(fù)被問(wèn)過(guò)其實(shí)答案并不復(fù)雜。首先畢設(shè)題目是“大數(shù)據(jù)”方向題目里明確出現(xiàn)了Hadoop和Spark這決定了技術(shù)棧必須包含分布式存儲(chǔ)與計(jì)算成分這不是說(shuō)SpringBoot不好而是題目的考點(diǎn)和重點(diǎn)不同。HadoopSpark體現(xiàn)的是大規(guī)模數(shù)據(jù)處理能力Django負(fù)責(zé)的是應(yīng)用層邏輯三者結(jié)合的側(cè)重點(diǎn)在于“數(shù)據(jù)的全鏈路流轉(zhuǎn)”。其次這套組合有天然的互補(bǔ)性。DjangoPython生態(tài)成熟做統(tǒng)計(jì)分析、寫(xiě)推薦算法腳本都方便Spark支持Python API和Django在語(yǔ)言層面統(tǒng)一減少了切換成本。真做起來(lái)你會(huì)發(fā)現(xiàn)Hadoop負(fù)責(zé)海量數(shù)據(jù)沉淀Spark負(fù)責(zé)把沉淀的數(shù)據(jù)變成指標(biāo)Django負(fù)責(zé)把指標(biāo)變成可交互的產(chǎn)品一條線走下來(lái)非常順。還有一個(gè)很現(xiàn)實(shí)的原因答辯的時(shí)候老師一定會(huì)問(wèn)“為什么這么選”。用這套組合能講出道理來(lái)——每個(gè)組件承擔(dān)獨(dú)立角色數(shù)據(jù)流清晰部署有層次感比單一框架的畢設(shè)有更好的展開(kāi)空間。1.3 系統(tǒng)做出來(lái)應(yīng)該長(zhǎng)什么樣我按實(shí)際項(xiàng)目的經(jīng)驗(yàn)把功能盤(pán)子分成五個(gè)模塊你照著這個(gè)清單做基本不會(huì)漏功能數(shù)據(jù)采集與存儲(chǔ)模塊獲取原始房源數(shù)據(jù)經(jīng)過(guò)清洗后寫(xiě)入HDFS同時(shí)將結(jié)構(gòu)化元數(shù)據(jù)同步到MySQL。數(shù)據(jù)分析模塊基于Spark實(shí)現(xiàn)區(qū)域均價(jià)、戶(hù)型分布、價(jià)格趨勢(shì)、熱門(mén)小區(qū)排行等核心指標(biāo)的計(jì)算??梢暬故灸K用ECharts展示分析結(jié)果包括柱狀圖、餅圖、折線圖、地圖熱力圖等。智能推薦模塊根據(jù)用戶(hù)瀏覽與收藏行為采用協(xié)同過(guò)濾推薦算法推薦可能感興趣的房源。用戶(hù)管理模塊注冊(cè)、登錄、收藏、瀏覽歷史管理這部分由Django直接實(shí)現(xiàn)。這五個(gè)模塊做下來(lái)論文有得寫(xiě)代碼有得展示答辯PPT也有層次感不會(huì)出現(xiàn)“做了個(gè)頁(yè)面但不知道干嘛”的情況。2. 從數(shù)據(jù)開(kāi)始采集、清洗與數(shù)倉(cāng)設(shè)計(jì)2.1 數(shù)據(jù)從哪里來(lái)三條正規(guī)路子說(shuō)起數(shù)據(jù)采集很多同學(xué)第一反應(yīng)就是寫(xiě)爬蟲(chóng)。我必須提醒一句爬蟲(chóng)這條路線風(fēng)險(xiǎn)很大尤其不能爬那些有明確平臺(tái)協(xié)議和數(shù)據(jù)保護(hù)的站點(diǎn)答辯也容易踩雷。做畢設(shè)數(shù)據(jù)來(lái)源完全有更穩(wěn)妥的替代方案公開(kāi)數(shù)據(jù)集一些高校和機(jī)構(gòu)提供了脫敏后的房屋租賃數(shù)據(jù)集雖然時(shí)效性不一定好但用來(lái)做分析完全夠用。合規(guī)的開(kāi)放接口部分房產(chǎn)信息平臺(tái)有開(kāi)發(fā)者開(kāi)放接口數(shù)據(jù)內(nèi)容經(jīng)過(guò)授權(quán)可以直接調(diào)用。模擬數(shù)據(jù)生成這是最可控的辦法自己寫(xiě)腳本按真實(shí)業(yè)務(wù)規(guī)律生成數(shù)據(jù)比如價(jià)格和面積的分布、戶(hù)型的占比、區(qū)域的熱度系數(shù)這樣數(shù)據(jù)量可以自由控制跑起Spark來(lái)也更有感覺(jué)。我手頭這個(gè)項(xiàng)目的數(shù)據(jù)就是用模擬腳本生成的生成了一萬(wàn)條房源記錄覆蓋了城市十個(gè)主要區(qū)域。這種做法的好處是數(shù)據(jù)量可以調(diào)整可以在開(kāi)發(fā)階段用小批量快速調(diào)試演示階段再切到大批量。2.2 數(shù)據(jù)字段與清洗規(guī)則無(wú)論數(shù)據(jù)來(lái)自哪條路子最后落到HDFS的文件格式都建議統(tǒng)一為CSV或Parquet。CSV的好處是瀏覽器和Excel都能直觀看到排錯(cuò)方便Parquet在Spark上的查詢(xún)效率高但不可讀。畢設(shè)階段用CSV足夠如果時(shí)間富余可以再轉(zhuǎn)存一份Parquet。核心字段建議這樣設(shè)計(jì)房源ID唯一標(biāo)識(shí)字符串類(lèi)型。小區(qū)名稱(chēng)字符串清洗時(shí)去除首尾空格、特殊符號(hào)。區(qū)域城市下轄區(qū)域用于分組統(tǒng)計(jì)。價(jià)格月租金數(shù)值類(lèi)型清洗時(shí)處理缺失值和異常值。面積平方米數(shù)值類(lèi)型。戶(hù)型臥室數(shù)客廳數(shù)例如“2室1廳”。朝向字符串如“南北通透”“南向”。樓層類(lèi)型低層/中層/高層方便做樓層偏好分析。租賃方式整租/合租。發(fā)布時(shí)間時(shí)間戳用于分析供應(yīng)趨勢(shì)。關(guān)注熱度數(shù)值可作為房源的受歡迎程度指標(biāo)。清洗規(guī)則是數(shù)據(jù)分析的老本行也是最容易被人忽略但價(jià)值最大的一步。原始數(shù)據(jù)里常見(jiàn)的問(wèn)題是字段缺失、格式不一致、取值范圍不合理。比如價(jià)格寫(xiě)成“面議”或者“2000-2500元/月”這種區(qū)間值都要統(tǒng)一轉(zhuǎn)成數(shù)值面積出現(xiàn)0平米需要剔除或填補(bǔ)區(qū)域字段可能有新舊叫法混雜要映射成同一套標(biāo)準(zhǔn)。我一般是這樣處理的價(jià)格缺失的中位數(shù)填充面積小于10或有明確亂碼的直接剔除區(qū)域的同義名稱(chēng)做一個(gè)映射表統(tǒng)一替換。2.3 數(shù)據(jù)存進(jìn)HDFS的目錄結(jié)構(gòu)HDFS的目錄設(shè)計(jì)看起來(lái)簡(jiǎn)單但對(duì)后面Spark讀取的便利性影響很大。建議按“數(shù)據(jù)層級(jí)業(yè)務(wù)日期”組織目錄比如/rent_hdfs /raw # 原始清洗后數(shù)據(jù) rent_data.csv /processed # Spark處理后的中間結(jié)果 area_price.csv house_type.csv我不建議把HDFS目錄層級(jí)設(shè)計(jì)得太深因?yàn)楫呍O(shè)的開(kāi)發(fā)調(diào)試基本在本地環(huán)境進(jìn)行目錄太復(fù)雜只會(huì)增加路徑拼接出錯(cuò)的風(fēng)險(xiǎn)。保持兩到三層一眼能看出“原始數(shù)據(jù)在哪、結(jié)果數(shù)據(jù)在哪”就夠了。3. Spark分析引擎讓數(shù)據(jù)說(shuō)話的核心3.1 離線分析任務(wù)的開(kāi)發(fā)套路Spark在整套系統(tǒng)里承擔(dān)的是“算”的角色任務(wù)類(lèi)型屬于離線批處理。實(shí)際操作中我發(fā)現(xiàn)很多人一上來(lái)就寫(xiě)復(fù)雜的RDD轉(zhuǎn)換其實(shí)大可不必。如果數(shù)據(jù)最終是結(jié)構(gòu)化表格直接用Spark SQLDataFrame API是最舒服的可讀性好統(tǒng)計(jì)分析也快。一個(gè)典型的分析任務(wù)流程是這樣的通過(guò)SparkSession讀取HDFS上的CSV文件。對(duì)DataFrame做字段類(lèi)型轉(zhuǎn)換、過(guò)濾異常數(shù)據(jù)。用groupBy、agg等算子完成聚合統(tǒng)計(jì)。將結(jié)果寫(xiě)成CSV或直接寫(xiě)入MySQL。我用的開(kāi)發(fā)環(huán)境是Hadoop 3.x Spark 3.xPython版本3.8以上本地模式跑小數(shù)據(jù)量完全夠用不需要在一開(kāi)始就搭集群。只要代碼最終能在偽分布式模式下讀取HDFS演示時(shí)數(shù)據(jù)流就是通的。3.2 核心分析指標(biāo)的計(jì)算邏輯我實(shí)際項(xiàng)目里做了五個(gè)核心指標(biāo)覆蓋了租房分析的各個(gè)維度區(qū)域平均租金按區(qū)域分組計(jì)算月租金的平均值展示哪個(gè)區(qū)最貴、哪里性?xún)r(jià)比高。戶(hù)型分布占比按“幾室?guī)讖d”分組統(tǒng)計(jì)數(shù)量反映市場(chǎng)主流供應(yīng)結(jié)構(gòu)。租金價(jià)格區(qū)間分布把價(jià)格分桶統(tǒng)計(jì)每個(gè)區(qū)間的房源數(shù)量比如“0-2000”“2000-4000”等用來(lái)觀察整體租金分布。熱門(mén)小區(qū)TOP10按小區(qū)房源數(shù)量降序排列提取前十名。租金隨發(fā)布時(shí)間的變化趨勢(shì)按月份聚合均價(jià)觀察價(jià)格隨時(shí)間波動(dòng)的情況。這些指標(biāo)看著簡(jiǎn)單但每一個(gè)都能在答辯時(shí)說(shuō)出一段業(yè)務(wù)解釋不是擺一個(gè)圖表就結(jié)束。比如區(qū)域均價(jià)可以結(jié)合數(shù)據(jù)講講哪個(gè)區(qū)域供應(yīng)集中、哪些位置租金中位數(shù)與平均值偏差大。這也是論文里很好的分析素材。3.3 從統(tǒng)計(jì)結(jié)果到MySQL的結(jié)果回寫(xiě)Spark算完的結(jié)果最終要交給Django去展示。這里有兩種做法一種是把Spark結(jié)果落盤(pán)成CSVDjango直接讀文件另一種是把結(jié)果寫(xiě)入MySQLDjango通過(guò)ORM查詢(xún)。我強(qiáng)烈推薦后者因?yàn)閃eb端查詢(xún)靈活用戶(hù)按條件篩選時(shí)不用每次重新讀文件也便于展示“實(shí)時(shí)數(shù)據(jù)更新”的效果?;貙?xiě)MySQL的代碼很簡(jiǎn)單用Spark的JDBC寫(xiě)入即可。需要注意驅(qū)動(dòng)配置和連接串格式同時(shí)表結(jié)構(gòu)要提前在MySQL里建好字段類(lèi)型盡量和Spark結(jié)果對(duì)齊避免類(lèi)型轉(zhuǎn)換的報(bào)錯(cuò)。df.write.format(jdbc) \ .option(url, jdbc:mysql://localhost:3306/rent_db) \ .option(dbtable, area_price) \ .option(user, root) \ .option(password, your_password) \ .option(driver, com.mysql.cj.jdbc.Driver) \ .mode(overwrite) \ .save()這里有一個(gè)小細(xì)節(jié)表名的順序要和你建表時(shí)的字段順序?qū)?yīng)上否則有可能出現(xiàn)數(shù)值錯(cuò)位。我在一開(kāi)始就吃過(guò)這個(gè)虧少了對(duì)字段名映射結(jié)果“區(qū)域”和“均價(jià)”兩列調(diào)換了看起來(lái)極其尷尬。4. 智能推薦系統(tǒng)協(xié)同過(guò)濾與ALS落地4.1 推薦系統(tǒng)的整體設(shè)計(jì)思路推薦系統(tǒng)是很多老師比較關(guān)注的亮點(diǎn)模塊。它不需要做到電商級(jí)別那么復(fù)雜但邏輯要完整、要能自圓其說(shuō)。我這個(gè)項(xiàng)目的推薦鏈路是“召回精排”兩段式召回階段根據(jù)用戶(hù)當(dāng)前篩選條件區(qū)域、戶(hù)型、價(jià)格上限從全量房源中篩出候選集這一步主要是縮小計(jì)算范圍。精排階段在候選集內(nèi)部使用基于ALS的協(xié)同過(guò)濾算法計(jì)算用戶(hù)對(duì)候選房源的預(yù)測(cè)評(píng)分按評(píng)分降序輸出TOP-N。為什么需要召回這一步因?yàn)閰f(xié)同過(guò)濾在數(shù)據(jù)稀疏的時(shí)候效果很不好。全量房源幾萬(wàn)條用戶(hù)行為記錄又少直接對(duì)整個(gè)矩陣做預(yù)測(cè)結(jié)果基本是亂的。先按業(yè)務(wù)規(guī)則篩一遍把候選集控制在幾百條以?xún)?nèi)再交給ALS排序效果會(huì)好很多而且訓(xùn)練速度也更快。4.2 把行為數(shù)據(jù)變成評(píng)分?jǐn)?shù)據(jù)ALS的輸入是“用戶(hù)ID、房源ID、評(píng)分”三列。但真實(shí)的用戶(hù)行為數(shù)據(jù)里沒(méi)有評(píng)分只有點(diǎn)擊、收藏、瀏覽這些動(dòng)作。所以第一步要做的是行為轉(zhuǎn)評(píng)分。我的規(guī)則是這樣的收藏房源計(jì)5分。點(diǎn)擊查看詳情計(jì)3分。直接瀏覽列表頁(yè)停留較久計(jì)1分。這樣設(shè)計(jì)是有道理的收藏代表強(qiáng)意圖點(diǎn)擊代表中等偏好瀏覽代表一般興趣。實(shí)現(xiàn)的時(shí)候只需要在Django里寫(xiě)一個(gè)信號(hào)邏輯用戶(hù)執(zhí)行這些操作時(shí)自動(dòng)在行為表中插入或更新評(píng)分記錄后面的推薦訓(xùn)練就有數(shù)據(jù)可用。class Behavior(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE) house models.ForeignKey(House, on_deletemodels.CASCADE) behavior_type models.CharField(max_length10) # click/favorite/browse score models.FloatField(default1.0) created_at models.DateTimeField(auto_now_addTrue)這里有個(gè)在畢設(shè)中幾乎一定會(huì)遇到的尷尬情況系統(tǒng)剛上線用戶(hù)很少行為數(shù)據(jù)為零。這種情況下協(xié)同過(guò)濾是廢的所以我的處理方案是用戶(hù)行為數(shù)據(jù)不足時(shí)系統(tǒng)自動(dòng)退化為“基于熱度的推薦”即按房源關(guān)注熱度、價(jià)格合適度、區(qū)域匹配度生成推薦列表當(dāng)行為數(shù)據(jù)積累到一定閾值后才切換到協(xié)同過(guò)濾。這個(gè)細(xì)節(jié)在答辯時(shí)一定要主動(dòng)講因?yàn)樗w現(xiàn)的是對(duì)真實(shí)業(yè)務(wù)問(wèn)題的認(rèn)知而不是只會(huì)調(diào)包。4.3 ALS模型訓(xùn)練與推薦結(jié)果生成使用Spark MLlib里的ALS做矩陣分解訓(xùn)練代碼非常簡(jiǎn)潔需要在代碼里指定三列特征列和最大迭代次數(shù)、正則化參數(shù)等超參數(shù)。這些參數(shù)不是越大越好我實(shí)測(cè)下來(lái)10到15輪迭代在小規(guī)模數(shù)據(jù)上已經(jīng)收斂正則化參數(shù)設(shè)在0.1左右效果比較穩(wěn)定具體還需要結(jié)合訓(xùn)練日志觀察。from pyspark.ml.recommendation import ALS from pyspark.ml.evaluation import RegressionEvaluator als ALS( userColuser_id, itemColhouse_id, ratingColrating, coldStartStrategydrop, maxIter12, regParam0.1 ) model als.fit(train_df)訓(xùn)練完成之后需要把模型產(chǎn)出的推薦結(jié)果保存下來(lái)供Django查詢(xún)。處理辦法是訓(xùn)練結(jié)束后為每個(gè)用戶(hù)生成TOP10推薦列表寫(xiě)入一張推薦結(jié)果表中。Django查詢(xún)這張表的時(shí)候只需要根據(jù)當(dāng)前登錄用戶(hù)的ID去取不需要在Web端重新跑一遍模型這樣響應(yīng)速度非常快。需要注意ALS有個(gè)“冷啟動(dòng)”問(wèn)題訓(xùn)練集里沒(méi)出現(xiàn)過(guò)的新房源模型是感知不到的。所以在推薦結(jié)果表更新的時(shí)候我會(huì)把新上架房源按熱度插到推薦列表的前幾位保證時(shí)效性。這個(gè)做法論文里能寫(xiě)系統(tǒng)層面也能明顯提升推薦結(jié)果的可接受度。5. Django可視化把結(jié)果給用戶(hù)看5.1 Django后端與API設(shè)計(jì)Django在項(xiàng)目里并不只是模板渲染它還承擔(dān)了數(shù)據(jù)服務(wù)接口的角色。我的設(shè)計(jì)是前后端通過(guò)JSON交互頁(yè)面負(fù)責(zé)渲染接口負(fù)責(zé)提供結(jié)構(gòu)化數(shù)據(jù)。這樣后續(xù)想換前端框架比如換成Vue或React后端代碼可以完全不動(dòng)。核心接口大概有這么幾個(gè)/api/summary/返回總體統(tǒng)計(jì)數(shù)據(jù)比如總房源數(shù)、平均租金、區(qū)域數(shù)。/api/area_price/返回區(qū)域均價(jià)列表用于柱狀圖。/api/house_type/返回戶(hù)型分布用于餅圖。/api/trend/返回價(jià)格隨時(shí)間變化趨勢(shì)用于折線圖。/api/recommend/返回當(dāng)前用戶(hù)的推薦房源列表。Django的視圖函數(shù)或類(lèi)視圖建議統(tǒng)一用JsonResponse返回?cái)?shù)據(jù)注意中文編碼問(wèn)題設(shè)置json_dumps_params{ensure_ascii: False}否則前端拿到的一串是\uXXXX的轉(zhuǎn)義字符排查起來(lái)非常費(fèi)勁。5.2 可視化大屏的實(shí)現(xiàn)要點(diǎn)可視化是大數(shù)據(jù)方向畢設(shè)最直觀的加分項(xiàng)頁(yè)面好不好看直接影響答辯印象分。我到最后的實(shí)現(xiàn)方案是ECharts Django模板渲染。ECharts使用非常簡(jiǎn)單引入JS文件后每個(gè)圖表對(duì)應(yīng)一個(gè)配置項(xiàng)。關(guān)鍵是從Django接口拿到JSON數(shù)據(jù)后動(dòng)態(tài)塞進(jìn)ECharts的配置里。比如區(qū)域均價(jià)圖fetch(/api/area_price/) .then(response response.json()) .then(data { var chart echarts.init(document.getElementById(areaPriceChart)); chart.setOption({ title: { text: 各區(qū)平均租金對(duì)比 }, tooltip: {}, xAxis: { data: data.areas }, yAxis: {}, series: [{ name: 均價(jià), type: bar, data: data.prices }] }); });頁(yè)面布局上我的建議是采用“大屏風(fēng)”上面一排核心KPI數(shù)字卡片中間幾個(gè)主力圖表地圖熱力圖放中間顯眼位置推薦列表放在右側(cè)作為輔助。頁(yè)面主色調(diào)可以選用偏深色的底突出數(shù)據(jù)的科技感。這里有個(gè)經(jīng)驗(yàn)不要用太多顏色統(tǒng)一色系再用一兩個(gè)高亮色強(qiáng)調(diào)重點(diǎn)區(qū)域即可否則頁(yè)面會(huì)顯得很“土”。5.3 推薦結(jié)果的展示邏輯推薦結(jié)果不是簡(jiǎn)單地列一堆房源卡片而是要讓用戶(hù)明白“為什么推薦給我”。我的做法是在每個(gè)推薦房源卡片上標(biāo)注推薦理由比如“位于你看過(guò)的XX區(qū)域”“價(jià)格接近你關(guān)注的區(qū)間”這個(gè)細(xì)節(jié)用戶(hù)的感知非常好也會(huì)成為答辯時(shí)的一個(gè)亮點(diǎn)。實(shí)現(xiàn)上并不復(fù)雜后端返回推薦房源的同時(shí)攜帶匹配到的區(qū)域或價(jià)格區(qū)間標(biāo)簽前端渲染卡片時(shí)放置在標(biāo)題上方。從工程角度看這只是多返回一個(gè)字段從產(chǎn)品角度看它讓推薦系統(tǒng)從黑盒變成了可解釋的白盒說(shuō)服力完全不一樣。對(duì)于未登錄用戶(hù)推薦接口返回空或返回?zé)衢T(mén)房源列表避免因拿不到用戶(hù)ID直接報(bào)錯(cuò)。這個(gè)兼容邏輯在實(shí)際運(yùn)行中很常見(jiàn)一定要提前處理好。6. 環(huán)境搭建、聯(lián)調(diào)與論文配套6.1 開(kāi)發(fā)環(huán)境搭建清單很多人的項(xiàng)目不是死在代碼上而是死在環(huán)境搭建上。大數(shù)據(jù)組件版本兼容性問(wèn)題非常多我把我穩(wěn)定運(yùn)行的一套組合列出來(lái)供你參考操作系統(tǒng)LinuxUbuntu 20.04/22.04或Windows WSL2。Java版本OpenJDK 1.8或11。Hadoop版本3.3.x配置偽分布式模式。Spark版本3.3.x默認(rèn)本地模式運(yùn)行配置讀取HDFS。Python環(huán)境3.8安裝pyspark、django、pymysql等依賴(lài)。MySQL版本8.x。重點(diǎn)提醒一句Hadoop和Spark的版本一定要匹配Spark的官方文檔會(huì)標(biāo)明編譯對(duì)應(yīng)的Hadoop版本。寧可都選穩(wěn)定版也不要追新否則網(wǎng)上都搜不到對(duì)應(yīng)的踩坑資料。安裝順序建議從下往上先裝JDK再裝Hadoop配置環(huán)境變量和SSH免密登錄啟動(dòng)HDFS后測(cè)試數(shù)據(jù)上傳下載然后裝Spark驗(yàn)證能讀取HDFS文件最后再裝Django和MySQL數(shù)據(jù)庫(kù)。每到一個(gè)階段就做一次冒煙測(cè)試不要把問(wèn)題積到最后一起爆發(fā)。6.2 部署聯(lián)調(diào)常見(jiàn)問(wèn)題速查表我把實(shí)際運(yùn)行中遇到過(guò)的高頻問(wèn)題整理成了表格幾乎每個(gè)都是踩坑換來(lái)的經(jīng)驗(yàn)建議你直接收藏對(duì)照現(xiàn)象可能原因解決辦法Spark連接HDFS超時(shí)偽分布式模式下HDFS未啟動(dòng)或端口配置錯(cuò)誤檢查start-dfs.sh是否成功執(zhí)行用jps查看NameNode和DataNode進(jìn)程Spark任務(wù)內(nèi)存溢出默認(rèn)executor內(nèi)存太小在spark-submit或代碼中設(shè)置spark.executor.memory2g待在本地跑就調(diào)大本地內(nèi)存MySQL中文亂碼數(shù)據(jù)庫(kù)字符集非utf8mb4建庫(kù)時(shí)指定charsetutf8mb4同時(shí)腳本中確保use_unicodeTrueDjango中導(dǎo)入pyspark報(bào)錯(cuò)Spark相關(guān)JAR包環(huán)境變量不對(duì)確認(rèn)SPARK_HOME配置正確且使用相同Python解釋器ECharts圖表顯示空白接口返回?cái)?shù)據(jù)格式不對(duì)或跨域先直接在瀏覽器訪問(wèn)接口看是否返回合法JSON再檢查ECharts配置項(xiàng)字段名推薦結(jié)果全是熱門(mén)房源用戶(hù)行為數(shù)據(jù)不足調(diào)整行為轉(zhuǎn)評(píng)分的規(guī)則或檢查訓(xùn)練集是否包含該用戶(hù)數(shù)據(jù)其中內(nèi)存溢出是我遇到最多的問(wèn)題。一開(kāi)始調(diào)了很大的executor內(nèi)存跑倒是能跑但換到配置較低的機(jī)器上就徹底崩潰。后來(lái)我調(diào)整了分區(qū)數(shù)和內(nèi)存參數(shù)的組合反而更快更穩(wěn)定。所以說(shuō)參數(shù)不是越大越好先理解了再調(diào)才不會(huì)被表象忽悠。6.3 LW、PPT與講解的準(zhǔn)備經(jīng)驗(yàn)畢設(shè)交付里除了源碼還有LW、PPT和講解三個(gè)部分。很多同學(xué)重代碼輕文檔結(jié)果倒是系統(tǒng)能跑答辯一緊張就講不出東西。這里說(shuō)說(shuō)我的經(jīng)驗(yàn)。LW也就是論文文檔結(jié)構(gòu)上建議按標(biāo)準(zhǔn)軟件工程文檔組織摘要、緒論、需求分析、系統(tǒng)設(shè)計(jì)、系統(tǒng)實(shí)現(xiàn)、系統(tǒng)測(cè)試、總結(jié)展望。重點(diǎn)要寫(xiě)清楚的是需求分析和系統(tǒng)設(shè)計(jì)前者體現(xiàn)你對(duì)業(yè)務(wù)的理解后者體現(xiàn)你的技術(shù)架構(gòu)能力。Hadoop、Spark這些組件可以引用一些原理性的描述但不要整段照抄要結(jié)合自己的代碼來(lái)寫(xiě)比如“本系統(tǒng)采用Spark SQL完成區(qū)域均價(jià)統(tǒng)計(jì)數(shù)據(jù)流如下……”這樣論文查重率也能控制住。PPT最忌諱的就是密密麻麻全是字。一個(gè)頁(yè)面上只要放一個(gè)核心點(diǎn)配合架構(gòu)圖、截圖和關(guān)鍵指標(biāo)即可。講解的重點(diǎn)排在“系統(tǒng)架構(gòu)圖——數(shù)據(jù)流程——推薦算法——可視化效果”這條線上不要一開(kāi)始就鉆到細(xì)枝末節(jié)里。你要給老師講的是一個(gè)完整的、能自洽的故事而不是一組零散的功能點(diǎn)。講解部分如果是錄制視頻要注意兩點(diǎn)一是演示流程要提前排練到流暢尤其是集群?jiǎn)?dòng)和頁(yè)面加載的等待時(shí)間二是如果中途報(bào)錯(cuò)寧可重錄也不要硬著頭皮繼續(xù)保證成片是“穩(wěn)”的。最后的一點(diǎn)實(shí)際心得這套項(xiàng)目做完我最深的感觸是它的難點(diǎn)不在任何一個(gè)單一技術(shù)上而在“把數(shù)據(jù)從HDFS流轉(zhuǎn)到Spark再到MySQL最后在頁(yè)面上呈現(xiàn)”的全鏈路貫通。你只要把這條鏈路走通把每個(gè)環(huán)節(jié)的關(guān)鍵問(wèn)題想清楚論文、答辯、演示都會(huì)順理成章。如果你正在做這個(gè)題目我的建議是先別急著裝環(huán)境、寫(xiě)代碼先把數(shù)據(jù)和業(yè)務(wù)搞明白。一萬(wàn)條數(shù)據(jù)長(zhǎng)什么樣用戶(hù)想看什么推薦系統(tǒng)怎么才算有效這些問(wèn)題想清楚了代碼只是水到渠成的事情。真正卡住你的從來(lái)不是某個(gè)API不會(huì)用而是沒(méi)想清楚這一步做完之后下一步要干什么。把鏈路理順剩下的就是耐心調(diào)試的事。