據(jù)可視化與銷量預(yù)測畢設(shè)項(xiàng)目全流程解析)
每年這個時候總有一批人被畢業(yè)設(shè)計(jì)折磨得睡不好覺。如果你點(diǎn)進(jìn)這個標(biāo)題大概率正在為到底做個什么題目既能過盲審、又能寫滿論文、還不至于把自己逼瘋而發(fā)愁。Python電商數(shù)據(jù)可視化分析與銷量預(yù)測這個方向可以說是我見過性價比極高的畢設(shè)選題之一它一個項(xiàng)目囊括了爬蟲采集、數(shù)據(jù)清洗、數(shù)據(jù)庫設(shè)計(jì)、Web應(yīng)用開發(fā)、可視化展示、機(jī)器學(xué)習(xí)建模和算法優(yōu)化一套流程下來既展示了完整的工程能力又有足夠的技術(shù)深度去應(yīng)付答辯。這篇文章我打算完全按照從零幫你把項(xiàng)目落地的思路來寫把我自己在實(shí)際開發(fā)這套系統(tǒng)時踩過的坑、試出來的經(jīng)驗(yàn)、以及最終定型的代方案都攤開來講。無論你是打算直接照著一個完整項(xiàng)目抄作業(yè)還是想搞清楚里面每個環(huán)節(jié)為什么要這樣設(shè)計(jì)這篇內(nèi)容都能給你一個明確的參考。1. 項(xiàng)目整體設(shè)計(jì)與技術(shù)選型1.1 系統(tǒng)架構(gòu)的三層拆分先把這個系統(tǒng)拆開看電商數(shù)據(jù)可視化分析與銷量預(yù)測到底是什么。它本質(zhì)上做的是三件事第一從公開電商頁面上把商品數(shù)據(jù)拿下來第二把拿到的數(shù)據(jù)整理干凈并存儲第三基于這些數(shù)據(jù)做圖表展示和未來銷量預(yù)測。對應(yīng)到代碼層面就是一個標(biāo)準(zhǔn)的三層架構(gòu)。數(shù)據(jù)采集層負(fù)責(zé)爬取商品名稱、價格、銷量、評論數(shù)、店鋪信息、上架時間等原始數(shù)據(jù)。這里我用的是Selenium框架它可以模擬真實(shí)瀏覽器操作能扛住大部分動態(tài)加載頁面的反爬限制。數(shù)據(jù)處理與存儲層把采集到的臟數(shù)據(jù)進(jìn)行清洗、去重、格式化然后存入數(shù)據(jù)庫?;A(chǔ)數(shù)據(jù)量級用MySQL就足夠但如果你想給系統(tǒng)加分、或者數(shù)據(jù)量真的很大可以在這一層把Hadoop生態(tài)中的Hive或HDFS引入進(jìn)來做分布式存儲和離線統(tǒng)計(jì)。應(yīng)用展示與預(yù)測層用Flask作為Web框架搭建后臺管理系統(tǒng)和前端可視化頁面。同時把訓(xùn)練好的機(jī)器學(xué)習(xí)模型封裝成預(yù)測接口用戶在頁面上選擇商品或者輸入時間范圍就能得到未來的銷量走勢。這個三層設(shè)計(jì)不是拍腦袋定的。做畢業(yè)設(shè)計(jì)有一個很重要的原則每一層都要有獨(dú)立的技術(shù)難點(diǎn)但又不能復(fù)雜到一個人做不完。上面的拆分剛好滿足每個模塊都可以單獨(dú)拎出來寫幾頁論文這個需求同時還避免了把所有代碼堆在一個腳本里、最后自己都看不下去的尷尬局面。1.2 為什么選了Flask而不是Django或Spring Boot這個問題我在做技術(shù)選型時糾結(jié)了很久最后選Flask不是因?yàn)樗荘ython社區(qū)里最好的Web框架而是因?yàn)樗鼘Ξ呍O(shè)場景來說是最合適的。Flask的核心優(yōu)勢在于輕。一個完整的可視化后臺用Flask只需要少量的路由代碼和模板渲染代碼就能跑起來。Django雖然自帶了Admin后臺、ORM權(quán)限體系這些現(xiàn)成的功能但是它的學(xué)習(xí)曲線比較陡并且很多內(nèi)置功能其實(shí)你根本用不上反而增加了項(xiàng)目的理解成本。Spring Boot就更不用說了Java系的重型框架對一個以Python為主線的項(xiàng)目來說屬于自己給自己挖坑。說得直白一點(diǎn)畢業(yè)設(shè)計(jì)的核心目標(biāo)是用最少的時間成本把該有的功能全部落地并能在答辯時講清楚。Flask的路由裝飾器、Jinja2模板引擎和靈活的擴(kuò)展生態(tài)恰好滿足這個目標(biāo)。我自己的項(xiàng)目里Flask負(fù)責(zé)三塊內(nèi)容一是提供可視化頁面的路由和數(shù)據(jù)接口二是接收前端圖表組件傳過來的篩選參數(shù)并返回JSON格式的數(shù)據(jù)三是調(diào)用訓(xùn)練好的預(yù)測模型并返回預(yù)測結(jié)果。1.3 Hadoop在項(xiàng)目里的真實(shí)角色選做加分項(xiàng)標(biāo)題里提了hadoop這個關(guān)鍵詞我在設(shè)計(jì)項(xiàng)目時也認(rèn)真考慮過到底要不要真上Hadoop。最后我的結(jié)論是可以作為數(shù)據(jù)處理的可選增強(qiáng)模塊但不要讓它占據(jù)主鏈路。原因是Hadoop本身是一套分布式生態(tài)涉及到HDFS文件存儲、YARN資源調(diào)度、MapReduce計(jì)算框架。如果你的商品數(shù)據(jù)量級在幾十萬條以內(nèi)MySQL配合索引優(yōu)化處理起來根本沒有壓力強(qiáng)行上Hadoop反而會讓項(xiàng)目變得臃腫寫代碼和調(diào)試的時間翻倍。但為什么標(biāo)題里還是要提它因?yàn)樗且粋€很好的加分點(diǎn)和答辯話題。你可以這樣設(shè)計(jì)在數(shù)據(jù)采集層定期把MySQL中的歷史數(shù)據(jù)同步到Hadoop的HDFS上然后用Hive寫一些簡單的SQL做離線統(tǒng)計(jì)比如計(jì)算每個品類的月度銷量匯總、價格波動區(qū)間。這些統(tǒng)計(jì)結(jié)果再回流到MySQL的匯總表中給可視化頁面提供數(shù)據(jù)支撐。這樣做的話你就有了一個完整的關(guān)系型數(shù)據(jù)庫分布式存儲的混合架構(gòu)既保證了實(shí)時查詢的性能又展示了大數(shù)據(jù)處理的能力。答辯時老師問你為什么用Hadoop你就能說出這套分層的理由這比空喊口號要扎實(shí)得多。2. 商品數(shù)據(jù)采集層Selenium的實(shí)踐與踩坑2.1 為什么選擇Selenium驅(qū)動瀏覽器采集電商平臺的數(shù)據(jù)加載方式和普通靜態(tài)網(wǎng)頁完全不同。你直接在代碼里用requests請求頁面URL拿到的往往是空殼的HTML——真正的內(nèi)容是頁面加載后用JavaScript異步請求接口才渲染出來的。想拿到這些數(shù)據(jù)有三種常見的思路第一種是直接分析頁面背后的API接口自己構(gòu)造參數(shù)去請求JSON數(shù)據(jù)。這個方法效率最高、對服務(wù)器壓力最小但需要你花時間逆向接口的加密參數(shù)和簽名邏輯某些平臺的加密算法還挺復(fù)雜。第二種是使用Selenium這類瀏覽器自動化工具啟動一個真實(shí)的瀏覽器去訪問頁面等待頁面渲染完再抓取DOM節(jié)點(diǎn)中的數(shù)據(jù)。這種方法實(shí)現(xiàn)簡單抗接口變動能力強(qiáng)缺點(diǎn)是速度慢、資源占用大。第三種是無頭瀏覽器加代理工具的組合方案屬于進(jìn)階玩法兼顧了部分效率和偽裝效果但配置復(fù)雜度也上來了。對于畢業(yè)設(shè)計(jì)階段我最推薦Selenium。原因很現(xiàn)實(shí)它的代碼邏輯直白就是打開頁面、等待加載、定位元素、提取文本這一套流程同時它繞開了大部分需要逆向JS加密的難題能讓你把精力放在后面的數(shù)據(jù)處理和預(yù)測模型上而不是栽在爬蟲這一關(guān)。2.2 Selenium實(shí)操定位策略與數(shù)據(jù)提取模板在寫采集腳本時最核心的一塊就是元素的定位和數(shù)據(jù)提取。以采集商品列表頁為例核心參考代碼如下from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time options webdriver.ChromeOptions() # 避免被識別為自動化工具的基礎(chǔ)配置 options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) driver webdriver.Chrome(optionsoptions) def fetch_product_list(page_url): driver.get(page_url) # 顯式等待關(guān)鍵元素出現(xiàn)不要用固定sleep硬等 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, div.product-item)) ) # 讓頁面充分渲染尤其是有滾動加載的頁面 for _ in range(3): driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) time.sleep(1) items driver.find_elements(By.CSS_SELECTOR, div.product-item) results [] for item in items: try: title item.find_element(By.CSS_SELECTOR, h3.product-title).text.strip() price item.find_element(By.CSS_SELECTOR, span.price).text.strip() sales item.find_element(By.CSS_SELECTOR, span.sales).text.strip() results.append({title: title, price: price, sales: sales}) except Exception: # 個別商品信息不全跳過 continue return results # 翻頁采集示例只采集一定頁數(shù)避免請求過快 for page in range(1, 6): url fhttps://example.com/search?keyword無線耳機(jī)page{page} data fetch_product_list(url) print(f第{page}頁采集到{len(data)}條數(shù)據(jù)) time.sleep(3)這里有幾個關(guān)鍵細(xì)節(jié)值得展開說一下。首先是顯式等待WebDriverWait而不是隱式等待或固定sleep。固定sleep的問題在于網(wǎng)絡(luò)快的時候浪費(fèi)大量時間空等網(wǎng)絡(luò)慢的時候頁面還沒加載完就開始找元素直接拋異常。合理設(shè)置為最多等待10秒頁面元素一旦出現(xiàn)就立刻執(zhí)行下一步。其次是滾動頁面。很多電商列表頁不是一次性把所有商品渲染出來的而是滾動到頁面底部才觸發(fā)加載更多。我在代碼里用execute_script強(qiáng)行下拉了三次每次間隔一秒這樣能確保動態(tài)加載的商品都出現(xiàn)在DOM里。最后是字段缺失的容錯處理。實(shí)際采集時經(jīng)常遇到某個商品缺價格或缺銷量如果不對每個字段單獨(dú)做try異常捕獲整個腳本會因?yàn)橐粋€臟數(shù)據(jù)而崩潰。上面的代碼里把每個字段的提取都包在內(nèi)層try里失敗就跳過這條保證采集任務(wù)的連續(xù)性。2.3 Agent代理池與請求頻率控制標(biāo)題里的Agent這個詞我理解成兩層含義一是采集請求需要通過代理IP來輪換出口地址避免某個IP因請求頻率過高被平臺限制二是從工程架構(gòu)來講可以把采集任務(wù)設(shè)計(jì)成多節(jié)點(diǎn)分布式采集每個采集節(jié)點(diǎn)就是一個Agent。畢設(shè)階段實(shí)現(xiàn)分布式采集有點(diǎn)大動干戈了但代理池的思路完全可以落地。Python的requests庫可以通過proxies參數(shù)傳入代理地址Selenium也可以在啟動瀏覽器時通過selenium-wire或Chrome的proxy擴(kuò)展來設(shè)置代理。具體做法是準(zhǔn)備一批代理IP列表每次請求隨機(jī)取一個使用。但要注意一點(diǎn)代理質(zhì)量參差不齊有些免費(fèi)代理連接超時嚴(yán)重反而拖慢了采集效率。我的建議是不要過度追求代理池的規(guī)模而是準(zhǔn)備一個小型的、經(jīng)過連通性測試的高質(zhì)量代理池就夠用了。同時絕對要控制請求頻率每采集一頁至少間隔2到3秒高峰期再拉長。提示在對合規(guī)性要求嚴(yán)格的場景中采集前一定要仔細(xì)閱讀目標(biāo)平臺的條款只采集公開可訪問的信息并控制請求頻率避免對目標(biāo)站點(diǎn)造成壓力。項(xiàng)目中建議只采集少量數(shù)據(jù)用于研究演示不要大規(guī)模、高頻率抓取。祝你的項(xiàng)目順順利利再說一句實(shí)用經(jīng)驗(yàn)。Selenium啟動的瀏覽器窗口是會被網(wǎng)站的前端JavaScript檢測到的常見的檢測手段是檢查navigator.webdriver屬性是否為true。上面的代碼里通過excludeSwitches參數(shù)做了一層基礎(chǔ)偽裝但如果你在實(shí)操中發(fā)現(xiàn)還是被識別可以再嘗試給瀏覽器注入一段JavaScript來覆蓋webdriver屬性。這一塊屬于道高一尺魔高一丈的對抗環(huán)節(jié)做到基礎(chǔ)偽裝加上頻率控制已經(jīng)夠用了不建議在畢設(shè)階段陷入太深。3. 數(shù)據(jù)清洗與特征工程3.1 真實(shí)數(shù)據(jù)有多臟清洗就有多重要我經(jīng)常在指導(dǎo)別人的時候強(qiáng)調(diào)一句話你后面預(yù)測模型的效果好不好從數(shù)據(jù)清洗這一步就已經(jīng)注定了。很多人采集完數(shù)據(jù)就急著寫模型結(jié)果訓(xùn)練出來的模型準(zhǔn)確率慘不忍睹然后開始懷疑算法不行——其實(shí)是數(shù)據(jù)根本沒過關(guān)。電商數(shù)據(jù)常見的臟數(shù)據(jù)問題我列幾個典型的你就明白了銷量字段是文本格式比如500人付款或月銷1.2萬需要正則表達(dá)式把數(shù)字提取出來并統(tǒng)一單位價格字段里混入了促銷活動標(biāo)識比如原價后面跟了個劃線價同一件商品出現(xiàn)在多個分類頁里造成重復(fù)記錄某些熱門商品數(shù)據(jù)被采集了多次但每次采集時價格和銷量都不同需要決定取最新還是取平均商品的標(biāo)題很長里面塞滿了各種營銷詞例如正品保障限時特惠對文本分析來說都是噪聲。針對這些情況我會在項(xiàng)目里專門寫一個數(shù)據(jù)清洗模塊處理流程如下import re import pandas as pd def clean_sales(value): # 示例把月銷1.2萬轉(zhuǎn)為12000把500人付款轉(zhuǎn)為500 if not isinstance(value, str): return value value value.replace(人付款, ).replace(月銷, ) if 萬 in value: return int(float(value.replace(萬, )) * 10000) return int(value) def clean_price(value): # 只保留數(shù)字和小數(shù)點(diǎn)去掉貨幣符號 match re.search(r\d\.?\d*, str(value)) return float(match.group()) if match else None df pd.read_csv(raw_products.csv) df[sales_num] df[sales_text].apply(clean_sales) df[price_num] df[price_text].apply(clean_price) df df.drop_duplicates(subset[title, shop_name], keeplast) df df.dropna(subset[price_num, sales_num]) df df[df[price_num] 0]這里面最關(guān)鍵的一步是去重邏輯。我自己的采集中發(fā)現(xiàn)同一個商品如果被多個關(guān)鍵詞的搜索結(jié)果共享會在數(shù)據(jù)里出現(xiàn)很多次。通過標(biāo)題加店鋪名的組合作為去重鍵保留最后采集到的那條記錄這是最穩(wěn)妥的做法因?yàn)樽詈笠淮尾杉臄?shù)據(jù)在時間上最新更能反映商品的當(dāng)前狀態(tài)。3.2 銷量預(yù)測的特征工程怎么做清洗完之后我們需要思考一個問題預(yù)測一件商品未來某天的銷量應(yīng)該用哪些信息作為輸入這就是特征工程要解決的事。我從實(shí)際項(xiàng)目中總結(jié)的經(jīng)驗(yàn)是這些特征通常分為四類第一類是商品基礎(chǔ)特征包括價格、評論數(shù)、評分、所屬品類、是否參加促銷。這類特征刻畫了商品自身的屬性是模型學(xué)習(xí)什么樣的商品好賣的主要依據(jù)。第二類是時間特征包括距離上架的天數(shù)、星期幾、是否節(jié)假日、是否月初月末。電商銷量有明顯的周期性周末通常高于工作日節(jié)假日前后促銷活動密集銷量會出現(xiàn)脈沖式上漲。如果不把時間特征告訴模型模型很難自己從數(shù)據(jù)里悟出這層規(guī)律。第三類是歷史銷量統(tǒng)計(jì)特征包括過去7天平均銷量、過去30天銷量標(biāo)準(zhǔn)差、最近一天的銷量變化率。這類特征本質(zhì)上是把時間序列的信息轉(zhuǎn)化為表格型特征也是預(yù)測效果最明顯的幾個特征之一。第四類是競爭環(huán)境特征包括同品類商品的平均價格、同品類商品的銷量排名百分位。商品之間的競爭關(guān)系會影響個體的銷量表現(xiàn)但這個特征在畢設(shè)階段可以做成簡化版本。這四類特征匯總之后每一行就是一個商品在某天的特征向量每一行的目標(biāo)值就是商品在該天的實(shí)際銷量。整理成這種格式之后后面的機(jī)器學(xué)習(xí)模型就能直接吃進(jìn)去了。3.3 MySQL與Hadoop的存儲劃分關(guān)于存儲我前面提到過MySQL為主、Hadoop為可選增強(qiáng)。這里具體說說數(shù)據(jù)落在MySQL時的表結(jié)構(gòu)設(shè)計(jì)。我會把數(shù)據(jù)分成三張表商品基本信息表product_info、歷史銷量明細(xì)表sales_history、數(shù)據(jù)采集日志表crawl_log。商品信息表存儲固定的屬性字段一張記錄對應(yīng)一個商品歷史銷量明細(xì)表是長表結(jié)構(gòu)一條記錄對應(yīng)一個商品在某個日期的銷量和價格采集日志表用來記錄每次采集的時間、數(shù)量、耗時、異常情況方便排查問題。在設(shè)計(jì)銷量明細(xì)表時一定要在商品ID和日期上建立聯(lián)合索引。因?yàn)楹罄m(xù)所有的特征工程查詢都是根據(jù)商品ID查它的歷史銷量序列沒有索引的話數(shù)據(jù)量上來之后一個簡單的查詢可能就要好幾秒鐘。至于Hadoop的引入方式我推薦一個輕量級做法每天采集結(jié)束后寫一個同步腳本把銷售明細(xì)表中的數(shù)據(jù)以追加的方式寫入HDFS路徑然后使用Hive創(chuàng)建一張外部表指向這個路徑。這樣你在論文里就可以寫道系統(tǒng)通過Hadoop生態(tài)實(shí)現(xiàn)了歷史數(shù)據(jù)的分布式存儲為后續(xù)擴(kuò)大數(shù)據(jù)規(guī)模提供了橫向擴(kuò)展能力。不需要在畢設(shè)階段真的用MapReduce寫復(fù)雜的分析任務(wù)但架構(gòu)的完整性和前瞻性已經(jīng)體現(xiàn)出來了。4. 可視化分析與銷量預(yù)測模型4.1 可視化框架選擇從ECharts到自繪圖表可視化是整個系統(tǒng)最直觀的部分也是答辯時最容易讓老師眼前一亮的模塊。我在項(xiàng)目里用的是ECharts搭配Python的Pyecharts封裝庫。如果你對前端技術(shù)不太熟悉用Pyecharts是明智的選擇你可以在Python代碼里直接鏈?zhǔn)秸{(diào)用圖表組件生成HTML文件或者嵌入到Flask的模板中。它支持折線圖、柱狀圖、餅圖、熱力圖、地圖等一系列常用圖表調(diào)用邏輯也很清晰。下面是一段生成銷量趨勢圖的示例from pyecharts.charts import Line from pyecharts import options as opts line ( Line() .add_xaxis(date_list) .add_yaxis(實(shí)際銷量, actual_sales, is_smoothTrue) .add_yaxis(預(yù)測銷量, predicted_sales, is_smoothTrue) .set_global_opts( title_optsopts.TitleOpts(title商品銷量趨勢與實(shí)際對比), tooltip_optsopts.TooltipOpts(triggeraxis), legend_optsopts.LegendOpts(pos_toptop), ) ) line.render(templates/charts/sales_trend.html)生成的HTML文件可以直接被Flask的模板引擎引用也可以利用render_embed()函數(shù)把圖表內(nèi)容直接嵌入頁面。實(shí)際項(xiàng)目里我會在頁面左側(cè)放商品篩選器右側(cè)放圖表區(qū)域用戶每切換一個商品前端就向后端發(fā)起異步請求后端返回該商品的銷量序列和預(yù)測結(jié)果前端動態(tài)更新圖表。除了折線圖還有幾個圖表是可視化的標(biāo)配價格區(qū)間分布用柱狀圖品類銷量占比用餅圖商品銷量排名用橫向條形圖。把這些圖表組合在一個數(shù)據(jù)看板頁面里給人的直觀感受就是這個項(xiàng)目做了完整的數(shù)據(jù)分析閉環(huán)這對論文評閱和答辯印象分都有不小的幫助。4.2 銷量預(yù)測模型怎么選從線性回歸到XGBoost銷量預(yù)測是本項(xiàng)目的算法核心也是論文里需要花大量篇幅去介紹的部分。我會建議你在項(xiàng)目里做多個模型的橫向?qū)Ρ榷皇侵慌芤粋€模型就交差。我先說最簡單也最穩(wěn)妥的基線模型——線性回歸。它的邏輯非常直白就是假設(shè)銷量和各個特征之間存在線性關(guān)系。線性回歸的效果通常不會特別驚艷但它有以下好處一是訓(xùn)練速度快幾乎不用調(diào)參二是結(jié)果可解釋性強(qiáng)每個特征前面的系數(shù)能直接說明該特征對銷量的影響方向和幅度三是可以作為一個基準(zhǔn)后續(xù)的復(fù)雜模型效果必須比它好這才有說服力。然后是樹模型這個應(yīng)該是你真正的主力模型。在實(shí)際項(xiàng)目中隨機(jī)森林和XGBoost或者LightGBM之間我更推薦直接上XGBoost因?yàn)樗鼘μ卣鞴こ痰囊笙鄬捜菽軌蜃詣硬蹲教卣髦g的非線性關(guān)系還帶正則化項(xiàng)防止過擬合。用一批特征數(shù)據(jù)訓(xùn)練XGBoost回歸模型核心代碼大概是這樣import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, r2_score X feature_df.drop(sales, axis1) y feature_df[sales] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model xgb.XGBRegressor( n_estimators300, max_depth6, learning_rate0.05, subsample0.8, colsample_bytree0.8 ) model.fit(X_train, y_train) y_pred model.predict(X_test) print(MAE:, mean_absolute_error(y_test, y_pred)) print(R2:, r2_score(y_test, y_pred))這里有幾個必須講清楚的參數(shù)n_estimators代表樹的數(shù)量太小了學(xué)習(xí)不充分太大了容易過擬合而且訓(xùn)練速度慢。我一般用300作為一個起點(diǎn)再結(jié)合早停機(jī)制確定最優(yōu)值。max_depth是樹的深度控制模型的復(fù)雜度太深會過擬合畢設(shè)數(shù)據(jù)量不大的話6左右是安全范圍。learning_rate是學(xué)習(xí)率調(diào)小一點(diǎn)能提升模型精度但相應(yīng)地需要更多樹來擬合0.05到0.1之間是常見的取值區(qū)間。subsample和colsample_bytree分別是行采樣和列采樣比例它們的存在能讓每棵樹看到的數(shù)據(jù)不完全相同增加模型的多樣性降低過擬合風(fēng)險(xiǎn)。以上這些都是可以寫進(jìn)論文的參數(shù)分析內(nèi)容比單純貼一段訓(xùn)練代碼要有深度得多。4.3 Deep Learning要不要硬上LSTM很多學(xué)生會糾結(jié)一個點(diǎn)時間序列預(yù)測是不是得上LSTM才算高級我的回答是看你的數(shù)據(jù)量。LSTM長短期記憶網(wǎng)絡(luò)是循環(huán)神經(jīng)網(wǎng)絡(luò)的一種變體非常擅長捕捉序列數(shù)據(jù)中的長期依賴關(guān)系。如果項(xiàng)目的銷量數(shù)據(jù)跨度超過一年、且是按天記錄的LSTM也許能比XGBoost多學(xué)到一些時間模式。但畢設(shè)項(xiàng)目讓你能采集到的數(shù)據(jù)往往只有近幾個月樣本量可能就幾百個時間點(diǎn)這種情況下LSTM不僅訓(xùn)練慢、調(diào)參復(fù)雜效果也未必比XGBoost好。我的建議是如果你想把項(xiàng)目拔高一個層次可以設(shè)計(jì)一個對比實(shí)驗(yàn)——在同樣的數(shù)據(jù)上分別訓(xùn)練線性回歸、XGBoost和LSTM三種模型用評估指標(biāo)展示各自的優(yōu)劣并分析造成差異的原因。最后得出結(jié)論在當(dāng)前小規(guī)模數(shù)據(jù)集上XGBoost在精度和穩(wěn)定性上表現(xiàn)最好LSTM雖然作為深度學(xué)習(xí)方法具備更強(qiáng)的序列建模能力但受限于數(shù)據(jù)規(guī)模和訓(xùn)練成本暫不采用。這個結(jié)論寫在論文里非常合理也體現(xiàn)了你對不同模型的認(rèn)知深度。4.4 算法優(yōu)化從網(wǎng)格搜索到特征篩選標(biāo)題里的算法優(yōu)化四個字落到具體操作上我總結(jié)為三個層面。第一個層面是超參數(shù)優(yōu)化。用網(wǎng)格搜索或者隨機(jī)搜索來尋找更優(yōu)的模型參數(shù)組合核心代碼是Sklearn的GridSearchCV。典型案例就是在max_depth、n_estimators、learning_rate等參數(shù)上定義候選值范圍然后用交叉驗(yàn)證去評估參數(shù)組合的表現(xiàn)。交叉驗(yàn)證的作用是防止參數(shù)恰好在一個測試集上表現(xiàn)好、換個數(shù)據(jù)就崩掉它把訓(xùn)練數(shù)據(jù)切成多份輪流驗(yàn)證結(jié)果更有說服力。第二個層面是特征選擇。不是所有特征都對預(yù)測有幫助有些特征甚至?xí)a(chǎn)生噪聲。常見做法有兩個一是用樹模型自帶的feature_importances_屬性查看特征重要性把排名靠后的特征去掉再訓(xùn)練一次二是用遞歸特征消除RFE或者基于正則化的方法篩選特征。實(shí)操中我發(fā)現(xiàn)歷史7天平均銷量和上架天數(shù)幾乎總是排在特征重要性的最前面而評論內(nèi)容長度這類文本型特征往往不重要。第三個層面是模型融合。如果時間充??梢宰龊唵蔚募訖?quán)融合把XGBoost和線性回歸的預(yù)測結(jié)果按比例加權(quán)相加。由于兩個模型的預(yù)測誤差模式不完全一致融合后的結(jié)果通常比單個模型更穩(wěn)定。這是一種很實(shí)用的工程技巧答辯時也能拿出實(shí)操數(shù)據(jù)來講解優(yōu)化前后的效果對比。5. 常見問題排查與實(shí)操技巧5.1 高頻問題速查表我把這個項(xiàng)目從零到一搭建過程中最容易踩的坑整理成了一張表都是我自己以及找我交流過方案的同學(xué)實(shí)測碰到的真問題。問題現(xiàn)象根本原因解決思路Selenium啟動后瀏覽器頁面空白Chrome驅(qū)動版本不匹配查看本地Chrome版本到驅(qū)動下載源下載對應(yīng)版本并配置環(huán)境變量頁面元素定位不到頁面未加載完或者元素在frame中換用WebDriverWait顯式等待檢查是否在iframe內(nèi)需要先切換frame采集的中文亂碼控制臺編碼問題或頁面編碼識別錯誤設(shè)置Python輸出編碼為utf-8響應(yīng)內(nèi)容指定正確字符集銷量字段五花八門電商頁面展示形態(tài)不統(tǒng)一寫多個正則規(guī)則覆蓋人付款月銷已售等常用格式MySQL插入數(shù)據(jù)報(bào)主鍵沖突商品重復(fù)采集使用INSERT ... ON DUPLICATE KEY UPDATE做增量更新訓(xùn)練模型時特征全是NaN數(shù)據(jù)清洗環(huán)節(jié)沒有處理缺失值對數(shù)值特征用中位數(shù)填充對類別特征用眾數(shù)填充預(yù)測結(jié)果全是同一個值模型欠擬合或特征與目標(biāo)值無關(guān)檢查特征與銷量的相關(guān)系數(shù)增加歷史銷量統(tǒng)計(jì)特征圖表在Flask頁面不顯示靜態(tài)資源路徑配置錯誤確認(rèn)Flask的static目錄指向包含ECharts的JS文件夾5.2 獨(dú)家避坑經(jīng)驗(yàn)第一永遠(yuǎn)不要企圖把采集、清洗、建模、展示一股腦寫完再調(diào)試。正確做法是分模塊開發(fā)每個模塊單獨(dú)能跑通了再串聯(lián)。你可能會覺得這樣慢實(shí)際上這是最快的路。一個模塊出問題時你能一眼定位到問題在哪而不是在幾百行雜亂的代碼里大海撈針。第二關(guān)于數(shù)據(jù)庫的設(shè)計(jì)哪怕你不確定后面會不會用到某些字段也建議先預(yù)留。我在項(xiàng)目中后期遇到過這種情況寫特征工程時需要用到商品上架日期但是原始表設(shè)計(jì)時沒加這個字段最后只能回頭重新采集數(shù)據(jù)。數(shù)據(jù)量小時還能忍受數(shù)據(jù)量大一點(diǎn)再補(bǔ)采時間和流量成本都很難受。第三整個系統(tǒng)里最不能出錯的環(huán)節(jié)不是模型而是數(shù)據(jù)字段的類型統(tǒng)一。特征矩陣?yán)锶绻麚诫s了字符串、空值和不同的數(shù)值精度模型訓(xùn)練時會報(bào)各種匪夷所思的錯。強(qiáng)烈建議在每個環(huán)節(jié)的末尾都打印一行DataFrame的信息摘要包含每列的數(shù)據(jù)類型和缺失值數(shù)量用不了多少時間卻能省掉無數(shù)排查過程的痛苦。第四答辯演示時一定要準(zhǔn)備一份穩(wěn)定的演示數(shù)據(jù)不要在演示現(xiàn)場臨時跑爬蟲。采集過程受網(wǎng)絡(luò)、目標(biāo)站點(diǎn)頁面結(jié)構(gòu)變化的影響太大一旦現(xiàn)場翻車會非常被動。正確做法是把采集好的數(shù)據(jù)固化到數(shù)據(jù)庫里演示時直接走讀數(shù)據(jù)庫→可視化→調(diào)模型預(yù)測的流程整個過程穩(wěn)定可控。6. 寫在最后的經(jīng)驗(yàn)總結(jié)這個項(xiàng)目做完之后我最大的體會是畢設(shè)項(xiàng)目不一定要追求大而全但一定要形成邏輯閉環(huán)。從數(shù)據(jù)采集到數(shù)據(jù)清洗從數(shù)據(jù)庫設(shè)計(jì)到圖表展示再到最后的銷量預(yù)測每一步都要能回答我為什么這樣做這個問題。能做到這一點(diǎn)論文的寫作反而就水到渠成了因?yàn)槊總€章節(jié)都有真實(shí)的工作內(nèi)容和決策過程可以寫。最后再分享一個小建議答辯之前自己把整個系統(tǒng)從數(shù)據(jù)采集開始完整跑一遍感受一下哪些環(huán)節(jié)最慢、哪些環(huán)節(jié)最容易出問題。然后針對性地寫一份簡短的系統(tǒng)說明文檔把技術(shù)架構(gòu)圖、數(shù)據(jù)流向、模型評估指標(biāo)這些核心信息放在第一頁。這份文檔不僅能幫你自己整理思路往往還能在答辯現(xiàn)場變成救命稻草——當(dāng)老師問到某個細(xì)節(jié)你一時想不起來時掃一眼文檔就能快速恢復(fù)記憶。做畢設(shè)是一場漫長的拉鋸戰(zhàn)但它同時也是你第一次獨(dú)立完成數(shù)據(jù)采集→數(shù)據(jù)處理→算法建?!到y(tǒng)落地完整鏈條的寶貴機(jī)會。希望這篇文章能給你一個清晰的路標(biāo)讓你少走一些我當(dāng)年走過的彎路。