![]()
賽題背景
本文分享賽題來自“中國石化第一屆人工智能創(chuàng)新大賽”,大賽由中國石油化工集團(tuán)有限公司主辦,聚焦能源化工產(chǎn)業(yè)。賽題覆蓋了產(chǎn)業(yè)鏈上中下游的不同任務(wù),包括勘探開發(fā)、煉化生產(chǎn)、油品銷售、智能營(yíng)銷和智能金融等關(guān)鍵支撐環(huán)節(jié)。
目前大賽全部10道賽題均已上線,開放數(shù)據(jù)集下載、提交評(píng)測(cè),來自全國頂級(jí)高校、科研院所以及人工智能領(lǐng)域研發(fā)企業(yè)的學(xué)者、研究人員、技術(shù)開發(fā)者將共同參與賽事角逐,集智創(chuàng)新。
本次大賽設(shè)置了豐厚的大賽獎(jiǎng)勵(lì),總獎(jiǎng)金80萬元人民幣,其中每個(gè)賽道獎(jiǎng)金8萬元人民幣,分設(shè)一等獎(jiǎng)、二等獎(jiǎng)、三等獎(jiǎng)。此外,獲獎(jiǎng)團(tuán)隊(duì)還將獲得組委會(huì)頒發(fā)的榮譽(yù)證書,有機(jī)會(huì)獲得名企實(shí)習(xí)機(jī)會(huì),作品孵化機(jī)會(huì)等,針對(duì)應(yīng)用潛力大的優(yōu)秀項(xiàng)目團(tuán)隊(duì)有機(jī)會(huì)獲得額外激勵(lì)。
大賽官網(wǎng):https://aicup.sinopec.com/?source=bl04
賽題Baseline
Baseline代碼下載:https://pan.baidu.com/s/1GYcrdGEE3H1cBgmtawgW7g?pwd=smv3
今天我們主要分享“基于AI的生產(chǎn)裝置質(zhì)量指標(biāo)預(yù)測(cè)”賽題。針對(duì)該賽題,我們將從以下四個(gè)方面來介紹:首先,會(huì)分享一下本次賽題的背景知識(shí);接下來,我們會(huì)看一下賽題涉及到的相關(guān)數(shù)據(jù);接著會(huì)介紹一些Baseline的方案,以及基于Baseline可以優(yōu)化的一些思路和可能的方向;最后,會(huì)針對(duì)本次賽題進(jìn)行一個(gè)簡(jiǎn)單的總結(jié)。
![]()
一、 賽題背景介紹
首先,我們來看一下本次賽題的背景介紹。本次比賽主要是為了預(yù)測(cè)一個(gè)產(chǎn)品的關(guān)鍵質(zhì)量指標(biāo)。這個(gè)任務(wù)在AI的應(yīng)用場(chǎng)景中,主要是通過預(yù)測(cè)產(chǎn)品的關(guān)鍵質(zhì)量指標(biāo),來提高產(chǎn)品在生產(chǎn)中的效益,或者說市場(chǎng)上的競(jìng)爭(zhēng)力。
傳統(tǒng)上對(duì)這類產(chǎn)品關(guān)鍵質(zhì)量指標(biāo)的監(jiān)測(cè)主要是通過人工定時(shí)采樣,然后將樣品送到實(shí)驗(yàn)室進(jìn)行化學(xué)分析。那么,通過人工手段有哪些缺點(diǎn)呢?
第一,化驗(yàn)過程非常耗時(shí),導(dǎo)致質(zhì)量監(jiān)測(cè)存在顯著的延時(shí)。當(dāng)某個(gè)產(chǎn)品被發(fā)現(xiàn)不合格時(shí),可能已經(jīng)造成了大量的物料浪費(fèi)和經(jīng)濟(jì)損失。因此,通過人工化驗(yàn),質(zhì)量監(jiān)控的周期其實(shí)非常長(zhǎng),會(huì)浪費(fèi)大量的生產(chǎn)物料并造成經(jīng)濟(jì)損失。
第二,人工采樣和化驗(yàn)的頻率是有限的,一般來說可能是一天一到兩次。所以,它其實(shí)很難實(shí)時(shí)把握我們生產(chǎn)過程中一些瞬時(shí)的波動(dòng)對(duì)產(chǎn)品質(zhì)量的影響,這在現(xiàn)代化的工業(yè)場(chǎng)景中是一個(gè)缺陷。
隨著工業(yè)互聯(lián)網(wǎng)和AI技術(shù)的發(fā)展,通過AI與智能制造的結(jié)合,可以大大彌補(bǔ)我們剛才提到的缺陷。由于工業(yè)互聯(lián)網(wǎng)的發(fā)展,大部分生產(chǎn)環(huán)境中都部署了大量的傳感器,可以達(dá)到秒級(jí)或者分鐘級(jí)的采集頻率,來實(shí)時(shí)檢測(cè)生產(chǎn)環(huán)境中的溫度、壓力、流量、液位等海量數(shù)據(jù)。這些數(shù)據(jù)可以不斷提高我們?cè)诋a(chǎn)品生產(chǎn)環(huán)境中質(zhì)量監(jiān)測(cè)的作用。
因此,本次賽題就是利用AI技術(shù),通過傳感器信息來預(yù)測(cè)產(chǎn)品的質(zhì)量,從而可以實(shí)時(shí)監(jiān)測(cè)質(zhì)量效果,減少產(chǎn)品的不合格率,提高產(chǎn)品質(zhì)量,同時(shí)也會(huì)降低對(duì)人工化驗(yàn)的依賴。這就是我們本次賽題的初衷,在工業(yè)互聯(lián)網(wǎng)環(huán)境中,這類需求非常大。
本次競(jìng)賽的任務(wù),就是依賴于傳感器的過程數(shù)據(jù),包括溫度、壓力、流量等,以及通過歷史實(shí)驗(yàn)室化驗(yàn)得到的質(zhì)量指標(biāo)數(shù)據(jù),來構(gòu)建模型。我們需要設(shè)計(jì)一個(gè)模型,通過給定的歷史傳感器數(shù)據(jù),對(duì)需要實(shí)時(shí)監(jiān)控的指標(biāo)進(jìn)行建模,從而精準(zhǔn)預(yù)測(cè)出每一個(gè)時(shí)間點(diǎn)對(duì)應(yīng)的關(guān)鍵質(zhì)量指標(biāo)數(shù)值。這就是我們本次賽題的背景,及其在實(shí)際生產(chǎn)環(huán)境中的應(yīng)用。
二、 數(shù)據(jù)集介紹
接下來我們看一下本次賽題提供的數(shù)據(jù)。賽題一共提供了幾個(gè)數(shù)據(jù)集,包含在三個(gè)文件夾中。這些數(shù)據(jù)是生產(chǎn)環(huán)境中的原始數(shù)據(jù),經(jīng)過脫敏處理。數(shù)據(jù)主要分為兩個(gè)部分:第一部分是過程數(shù)據(jù),也就是傳感器數(shù)據(jù),它包含一些蒸餾數(shù)據(jù)和反應(yīng)時(shí)的數(shù)據(jù),這些數(shù)據(jù)是通過傳感器進(jìn)行秒級(jí)傳輸?shù)玫降摹5诙糠质腔?yàn)數(shù)據(jù),主要來源于實(shí)驗(yàn)室歷史積累的化驗(yàn)結(jié)果。大家下載數(shù)據(jù)后可以看到,一共有三個(gè)這樣的文件夾。
首先第一個(gè)是反應(yīng)數(shù)據(jù),這個(gè)數(shù)據(jù)包含時(shí)間戳和一系列傳感器讀數(shù)。它的時(shí)間數(shù)據(jù)精確到秒級(jí),主要來源于傳感器的自動(dòng)傳輸。后面這些列是來自不同傳感器的數(shù)據(jù)。雖然官方?jīng)]有給出這些數(shù)據(jù)的具體物理意義,但我們其實(shí)可以大致推斷它們的含義,這一點(diǎn)我們稍后會(huì)介紹。
第二個(gè)數(shù)據(jù)是蒸餾數(shù)據(jù),這個(gè)數(shù)據(jù)與反應(yīng)數(shù)據(jù)類似,同樣是通過傳感器秒級(jí)傳輸?shù)玫降模舶藭r(shí)間戳和一系列傳感器讀數(shù)。
最后一個(gè)是歷史化驗(yàn)數(shù)據(jù)。例如,我們?cè)谀硞€(gè)時(shí)間點(diǎn)進(jìn)行采樣,樣品是“尾油”,然后我們可以檢測(cè)出它在不同階段的流出溫度、流出量等,通過樣品檢測(cè)得到它的分析數(shù)值。后面一列是當(dāng)前點(diǎn)的采樣時(shí)間。
從數(shù)據(jù)量上來看,前兩個(gè)傳感器傳輸?shù)臄?shù)據(jù)量比較大,有幾百兆,而化驗(yàn)數(shù)據(jù)則相對(duì)較少。這里也描述了詳細(xì)的數(shù)據(jù)信息,傳感器數(shù)據(jù)是實(shí)時(shí)傳輸?shù)模恳恍写硪粋€(gè)時(shí)間點(diǎn),采樣頻率是每30秒一次。數(shù)據(jù)文件包含兩種,以train為結(jié)尾的是訓(xùn)練集,以validation為結(jié)尾的是驗(yàn)證集。我們初賽的核心任務(wù)是預(yù)測(cè)validation數(shù)據(jù)集中的分析值,所以train中的分析值是我們的訓(xùn)練目標(biāo)(y值),而validation中的樣本是我們的測(cè)試集。
![]()
關(guān)于化驗(yàn)數(shù)據(jù),它的場(chǎng)景是這樣的:我們剛才提到有采樣時(shí)間,比如我們是早上7:00進(jìn)行采樣,那么采樣得到的樣品,其實(shí)是來源于這之前一段時(shí)間機(jī)器工作中產(chǎn)生的產(chǎn)品。所以,我們需要去匹配在7點(diǎn)之前的生產(chǎn)環(huán)境中的實(shí)時(shí)傳感器數(shù)據(jù)。例如,我們可以去匹配早上6:00到7:00的傳感器數(shù)據(jù),這個(gè)數(shù)據(jù)就可以當(dāng)作是當(dāng)前樣品在生產(chǎn)環(huán)境中的實(shí)時(shí)數(shù)據(jù)。
這份化驗(yàn)數(shù)據(jù)中還提供了一些指標(biāo),比如“初餾點(diǎn)”、“10%餾出溫度”、“50%餾出溫度”等不同的餾出點(diǎn)的溫度,以及“終餾點(diǎn)”等內(nèi)容。我們主要是預(yù)測(cè)樣品在不同指標(biāo)下的分析結(jié)果。最后,表里有一個(gè)“分析值”列,這個(gè)分析值其實(shí)就是我們的y值,也就是我們需要預(yù)測(cè)的目標(biāo)。我們需要預(yù)測(cè)這個(gè)樣品在不同指標(biāo)下的分析值,這個(gè)分析值就代表了它的質(zhì)量。
所以,我們的核心任務(wù)是基于化驗(yàn)數(shù)據(jù)中的X(采樣信息)和y(分析值)來進(jìn)行建模,建模之后,再根據(jù)驗(yàn)證集(validation)里的X來預(yù)測(cè)我們需要的y。我們把預(yù)測(cè)的y提交上去,就可以得到最終的比賽結(jié)果。化驗(yàn)數(shù)據(jù)同樣也包含以train結(jié)尾的訓(xùn)練集文件和以validation結(jié)尾的驗(yàn)證集文件。
![]()
最后是提交格式,我們需要把validation里面的每一條ID都預(yù)測(cè)出一個(gè)預(yù)測(cè)值,然后將預(yù)測(cè)值按照指定的CSV格式進(jìn)行提交,文件以逗號(hào)為分割。這就是我們整體的數(shù)據(jù)介紹,大家可以去官網(wǎng)上直接下載數(shù)據(jù)。
![]()
本次比賽使用的評(píng)價(jià)指標(biāo)是MAPE,即平均絕對(duì)百分比誤差。MAPE是衡量預(yù)測(cè)值與實(shí)際值之間差異的一個(gè)指標(biāo)。我們預(yù)測(cè)的是一個(gè)數(shù)值,所以我們用當(dāng)前真實(shí)值減去它的預(yù)測(cè)值,再除以真實(shí)值,然后取絕對(duì)值,以此作為衡量結(jié)果的指標(biāo)。當(dāng)真實(shí)值跟預(yù)測(cè)值越來越接近時(shí),代表我們預(yù)測(cè)的效果越來越好,整個(gè)模型效果也就會(huì)越來越好。所以,如果大家的MAPE值越來越小,就代表模型效果越來越好。大家需要注意本次比賽的評(píng)價(jià)指標(biāo),因?yàn)樵诓煌馁愵}里,評(píng)價(jià)指標(biāo)和優(yōu)化目標(biāo)都可能不一樣。
![]()
三、Baseline方案構(gòu)建
整體數(shù)據(jù)介紹完之后,我們接下來看一下如何構(gòu)建一個(gè)Baseline。
構(gòu)建Baseline的過程主要分為以下幾個(gè)部分:首先是數(shù)據(jù)理解和處理;其次是數(shù)據(jù)分析,這可能需要大家有一些業(yè)務(wù)知識(shí)的了解,比如分析蒸餾過程和反應(yīng)過程中的數(shù)值對(duì)我們最終分析值(y值)的影響;接下來是特征構(gòu)造的過程,我們將探討如何構(gòu)造特征;最后是建模,選擇使用機(jī)器學(xué)習(xí)模型還是時(shí)序模型。
![]()
1.數(shù)據(jù)理解與分析
這個(gè)過程就是對(duì)我們剛才講到的數(shù)據(jù)做一些簡(jiǎn)單的分析。我們最終的輸入是X和y,這兩張表非常重要,一張是反應(yīng)傳感器得到的數(shù)據(jù),另一張是蒸餾時(shí)傳感器得到的數(shù)據(jù)。這兩個(gè)數(shù)據(jù)主要用于我們本次賽題的特征構(gòu)建,因?yàn)閭鞲衅鞯膫鬏敂?shù)據(jù)會(huì)影響最終樣品的質(zhì)量,對(duì)我們的X有一個(gè)影響。
我們首先看一下我們的X。它是根據(jù)“樣品”以及當(dāng)前的“組份”得到它的y值。所以,“樣品”和“組份”這兩個(gè)字段你可以認(rèn)為是我們的ID。而y值就是分析值,比如我們要預(yù)測(cè)“尾油”在“10%餾出溫度”這個(gè)組份下的分析值是311.40攝氏度,這就是一個(gè)X和y的構(gòu)建。
為了更好地優(yōu)化賽題,大家可能需要了解一下各個(gè)組份的含義。比如,“初餾點(diǎn)”代表第一滴餾出液出現(xiàn)的溫度;后面是10%、30%、50%、70%、90%等不同百分比的餾出液體積對(duì)應(yīng)的溫度。大家可以看到,隨著餾出百分比不斷升高,它的溫度也是不斷變大的。后面還會(huì)預(yù)測(cè)一些“全餾量”(單位是毫升),以及一些“殘留量”等等。所以,大家需要了解組份的含義。
另外一個(gè)是“樣品”字段,它其實(shí)是采集了不同生產(chǎn)階段的樣品,比如“尾油”、“常二線”、“煤油”、“航煤”等。我們需要理解X和y的關(guān)系,分析值就是不同組份下的一個(gè)結(jié)果。我們的核心任務(wù)是,當(dāng)拿到一個(gè)樣品和組份時(shí),預(yù)測(cè)它對(duì)應(yīng)的分析值是多少。
舉個(gè)例子,以“初餾點(diǎn)”為例,它預(yù)測(cè)的分析值(比如231度)其實(shí)是會(huì)受到蒸餾傳感器和反應(yīng)傳感器的信息影響的。假如我們當(dāng)前采樣時(shí)間是早上7點(diǎn),我們就會(huì)根據(jù)傳感器的一個(gè)時(shí)間窗口,比如早上5:30到7點(diǎn)這個(gè)窗口內(nèi)的傳感器數(shù)據(jù),來判斷它的初餾點(diǎn)溫度是多少。因此,我們就需要用到傳感器數(shù)據(jù)表。我們需要看到當(dāng)前時(shí)間點(diǎn)往前推90分鐘,或者兩個(gè)小時(shí),或者一個(gè)小時(shí)的傳感器變化,這個(gè)窗口大小取決于自己對(duì)于特征的構(gòu)建。
根據(jù)一些實(shí)際生產(chǎn)環(huán)境的經(jīng)驗(yàn),在不同的餾出溫度下,它所依賴的蒸餾時(shí)間窗口其實(shí)是不一樣的。比如,后面的一些高餾出溫度,可能受到前面更長(zhǎng)時(shí)間段的傳感器窗口的影響。而對(duì)于反應(yīng)窗口而言,它同樣也有一個(gè)影響窗口。比如早上7點(diǎn)采樣,可能往前推10分鐘到兩個(gè)小時(shí)的反應(yīng)窗口數(shù)據(jù),對(duì)于“初餾點(diǎn)”的影響是比較大的。
這個(gè)“窗口”的選擇,是本次賽題一個(gè)非常關(guān)鍵的部分,它對(duì)于最終結(jié)果的影響非常大。另外,對(duì)于不同的樣品類型,這個(gè)窗口的大小其實(shí)也是有變化的,這個(gè)大家可以在后面去探索。
在我們構(gòu)建Baseline時(shí),可以先用一個(gè)統(tǒng)一的窗口。比如,蒸餾窗口我們統(tǒng)一使用采樣前的兩個(gè)小時(shí),反應(yīng)窗口我們使用采樣前180分鐘到10分鐘這個(gè)區(qū)間。
![]()
當(dāng)我們選定了窗口之后,就可以得到這樣的一個(gè)數(shù)據(jù)結(jié)果。比如,當(dāng)前采樣是早上7點(diǎn),我們就可以拿到往前90分鐘內(nèi)傳感器傳輸過來的數(shù)據(jù)變化。比如,T3000.PV這個(gè)特征,在90分鐘的窗口內(nèi),它會(huì)有一系列的值。右邊是反應(yīng)傳感器的數(shù)據(jù),比如我們是早上7點(diǎn)采樣,就往前推10分鐘到兩個(gè)小時(shí),可以看到T2000.PV這個(gè)特征下,它的一個(gè)變化結(jié)果。
有時(shí)候,我們可以通過特征的名稱來推斷它的物理含義。比如以T開頭的特征,官方雖然說做了匿名處理,但我們有時(shí)可以推斷出T可能代表溫度(Temperature)的簡(jiǎn)稱。同理,P可能是壓力(Pressure),F(xiàn)可能是流量(Flow),R可能是一個(gè)比值或者回流(Ratio/Reflux)。理解特征的含義有什么作用呢?它主要是幫助我們后面做特征交叉。例如,如果T開頭的都是溫度,我們就可以計(jì)算不同位置上傳感器的溫差,以此來判斷溫度變化的影響。
2.特征工程
基于我們剛剛構(gòu)建的這些窗口內(nèi)的數(shù)據(jù),我們就可以做一些特征了。我們知道,“樣品”和“組份”這兩個(gè)ID本身就可以作為特征。同時(shí),官方也提到,采樣時(shí)間和之前的傳感器傳輸時(shí)間是高度相關(guān)的。那么,我們就需要基于采樣時(shí)間往前推一個(gè)窗口(比如90分鐘或兩個(gè)小時(shí)),來得到傳感器的時(shí)序特征。
基于這個(gè)時(shí)間窗口內(nèi)的特征,我們需要做一些聚合操作。這里我們羅列了一些常用的特征聚合方法,它們可以表示特征的分布、最大最小變化,衡量這個(gè)時(shí)間窗口內(nèi)波形圖的變化。例如:
均值或標(biāo)準(zhǔn)差:可以衡量傳感器在這個(gè)階段的波動(dòng)性和穩(wěn)態(tài)。
分位數(shù):可以統(tǒng)計(jì)它的分布形狀。
極大值/極小值:可以判斷是否存在越界風(fēng)險(xiǎn),比如溫度過高或過低,可能會(huì)嚴(yán)重影響樣品的質(zhì)量。
滾動(dòng)均值或滾動(dòng)標(biāo)準(zhǔn)差:可以探索不同時(shí)間尺度下特征的影響。
我們還可以做一些特征交叉。剛才我們提到了,可以推斷出特征的類型。比如,有很多以T為開頭的特征,我們假設(shè)它們都是溫度,那么T3000和T3002很可能是安裝在不同位置上的傳感器,返回不同位置的溫度。這樣,我們就可以統(tǒng)計(jì)不同位置上的溫度變化,比如計(jì)算一些頂溫和塔板溫的差值,再對(duì)這個(gè)差值進(jìn)行統(tǒng)計(jì),這就是一個(gè)特征交叉的過程。
3. Baseline代碼實(shí)現(xiàn)
我們來看一下我們的Baseline是如何構(gòu)建的。這里我寫了一份Baseline的代碼。
首先,需要下載官方文件,解壓之后可以看到這樣的目錄結(jié)構(gòu)。其中主要有三個(gè)用于訓(xùn)練的數(shù)據(jù)文件:一個(gè)是反應(yīng)傳感器的訓(xùn)練數(shù)據(jù),一個(gè)是蒸餾的傳感器數(shù)據(jù),以及我們的訓(xùn)練集X和y。我們需要預(yù)測(cè)的是驗(yàn)證集的y,并且提供了一個(gè)提交樣例。
接下來,我們需要導(dǎo)入一些庫。這里我們主要選擇基于機(jī)器學(xué)習(xí)的方法,做一些特征工程,然后實(shí)現(xiàn)模型的構(gòu)建。我們用到了LightGBM這個(gè)模型,它在機(jī)器學(xué)習(xí)比賽中是一個(gè)比較常用的樹模型,泛化能力比較強(qiáng)。
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from lightgbm import LGBMRegressor
from sklearn.model_selection import KFold, cross_val_predict
from sklearn.metrics import mean_absolute_percentage_error第一步是讀取數(shù)據(jù),主要讀取訓(xùn)練集和驗(yàn)證集的數(shù)據(jù),總共有6個(gè)文件。讀取完成后,需要做一些數(shù)據(jù)處理。這里我們對(duì)y值做了一些處理,判斷并去掉了一些異常值。然后,我們把樣本中的中文列名轉(zhuǎn)化成英文列名,便于后續(xù)代碼處理,比如“采樣時(shí)間”改為sample_time,“分析值”改為value等。
analysis_train = pd.read_csv("train+validation+sample /analysis_train.csv")
analysis_validation_without_truth = pd.read_csv("train+validation+sample /analysis_validation_without_truth.csv")
distillation_process_train = pd.read_csv("train+validation+sample /distillation_process_train.csv")
distillation_process_validation = pd.read_csv("train+validation+sample /distillation_process_validation.csv")
reaction_process_train = pd.read_csv("train+validation+sample /reaction_process_train.csv")
reaction_process_validation = pd.read_csv("train+validation+sample /reaction_process_validation.csv")def is_numeric(value):
try:
float(value)
return True
except (ValueError, TypeError):
return False
analysis_train = analysis_train[analysis_train["分析值"].apply(is_numeric)]接著,我們對(duì)sample_time列做了一個(gè)時(shí)間類型的轉(zhuǎn)化和排序。
# 1) 統(tǒng)一字段名(中文→英文便于處理)
rename_map = {"采樣時(shí)間":"sample_time","組分":"component","分析值":"value","采樣點(diǎn)":"sample_point","樣品":"sample"}
analysis_train = analysis_train.rename(columns={c: rename_map.get(c,c) for c in analysis_train.columns})
analysis_validation_without_truth = analysis_validation_without_truth.rename(columns={c: rename_map.get(c,c) for c in analysis_validation_without_truth.columns})
analysis_train["sample_time"] = pd.to_datetime(analysis_train["sample_time"], errors="coerce")
analysis_validation_without_truth["sample_time"] = pd.to_datetime(analysis_validation_without_truth["sample_time"], errors="coerce")
# 過程表:把“時(shí)間”→ timestamp,并排好序
for df in (distillation_process_train, distillation_process_validation, reaction_process_train, reaction_process_validation):
df.rename(columns={"時(shí)間":"timestamp"}, inplace=True)
df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
df.dropna(subset=["timestamp"], inplace=True)
df.sort_values("timestamp", inplace=True)
df.drop_duplicates(subset=["timestamp"], inplace=True)
df.reset_index(drop=True, inplace=True)處理好X和y的數(shù)據(jù)之后,我們需要來構(gòu)造特征。我們知道,傳感器數(shù)據(jù)表和化驗(yàn)數(shù)據(jù)表之間的關(guān)聯(lián)是通過“采樣時(shí)間”來實(shí)現(xiàn)的。我們需要對(duì)采樣時(shí)間設(shè)定一個(gè)窗口,我們認(rèn)為在這個(gè)窗口內(nèi)的傳感器數(shù)據(jù)對(duì)于我們的y值是有意義的。這里設(shè)定了兩個(gè)窗口,一個(gè)是蒸餾窗口,一個(gè)是反應(yīng)窗口。
# 2) 固定窗口(簡(jiǎn)化):蒸餾 [T-120, T];反應(yīng)[T-180, T-10]
WD, OD = 120, 0
WR, OR = 180, 10然后,我們把需要用到的傳感器特征列名提取出來,比如蒸餾的特征列和反應(yīng)的特征列。
# 3) 選數(shù)值型傳感器列(排除 timestamp 等)
ignore = {"timestamp"}
d_cols_tr = [c for c in distillation_process_train.columns if c not in ignore ]
r_cols_tr = [c for c in reaction_process_train.columns if c not in ignore ]
d_cols_va = [c for c in distillation_process_validation.columns if c not in ignore ]
r_cols_va = [c for c in reaction_process_validation.columns if c not in ignore ]
# 統(tǒng)一列集合(避免推理缺列)
d_cols = sorted(list(set(d_cols_tr) & set(d_cols_va)))
r_cols = sorted(list(set(r_cols_tr) & set(r_cols_va)))接下來就到了核心的過程。我們首先需要把所有的時(shí)間數(shù)據(jù)取出來,然后將這個(gè)時(shí)間數(shù)據(jù)與我們的反應(yīng)傳感器數(shù)據(jù)和蒸餾傳感器數(shù)據(jù)進(jìn)行關(guān)聯(lián)。我們寫了一個(gè)函數(shù)來實(shí)現(xiàn)這個(gè)關(guān)聯(lián)。這個(gè)函數(shù)會(huì)拿到每個(gè)樣本的采樣時(shí)間t,然后根據(jù)設(shè)定的窗口大小,計(jì)算出開始時(shí)間和結(jié)束時(shí)間,再用這個(gè)時(shí)間窗口對(duì)反應(yīng)和蒸餾數(shù)據(jù)進(jìn)行切片,我們認(rèn)為這個(gè)時(shí)間段內(nèi)的數(shù)據(jù)對(duì)t時(shí)刻的樣本是有作用的。
之后就到了特征構(gòu)建的核心函數(shù)。我們需要對(duì)切片出來的反應(yīng)時(shí)間和蒸餾時(shí)間數(shù)據(jù)進(jìn)行特征構(gòu)建。在這里,我們主要構(gòu)建了三個(gè)例子性的特征:第一個(gè)是當(dāng)前窗口內(nèi)數(shù)據(jù)的平均值,第二個(gè)是它的方差,以及第三個(gè)是傳感器在窗口內(nèi)最后一個(gè)時(shí)刻的數(shù)據(jù)。也就是說,我們對(duì)這個(gè)時(shí)間窗口內(nèi)的數(shù)據(jù)求了均值和方差,并且還取了最后一個(gè)時(shí)刻的值。大家如果后面想做一些探索,可以在這里增加更多的特征,來反映整個(gè)窗口內(nèi)的曲線變化。構(gòu)建好的特征,我們把它聚合到一個(gè)字典里。
def build_feature_row(
T, distill_df, react_df, d_cols, r_cols,
WD=120, OD=0, WR=180, OR=10, target=np.nan, row_id=None
):
if pd.isna(T):
return None
# 窗口
d_start, d_end = T - pd.Timedelta(minutes=WD), T - pd.Timedelta(minutes=OD)
r_start, r_end = T - pd.Timedelta(minutes=WR), T - pd.Timedelta(minutes=OR)
# 切片
d_seg = distill_df[(distill_df["timestamp"]>=d_start) & (distill_df["timestamp"]<=d_end)]
r_seg = react_df[(react_df["timestamp"]>=r_start) & (react_df["timestamp"]<=r_end)]
def stats_block(seg, cols, prefix):
# 只用交集列,避免“補(bǔ)出來”的空列
use_cols = [c for c in cols if c in seg.columns]
if len(use_cols) == 0 or seg.empty:
return {f"{prefix}{c}_{s}": np.nan for c in cols for s in ["mean","std","last"]} | {
f"{prefix}points": 0
}
# 轉(zhuǎn)數(shù)值(把'—'、'NULL'、'開/關(guān)'等都變成 NaN 或數(shù)字后再算)
blk = seg[use_cols].apply(pd.to_numeric, errors="coerce")
m = blk.mean().add_prefix(prefix).add_suffix("_mean")
s = blk.std().add_prefix(prefix).add_suffix("_std")
l = blk.iloc[-1].add_prefix(prefix).add_suffix("_last")
return (pd.concat([m, s, l]).to_dict() |
{f"{prefix}points": len(blk)})
feats = {
"sample_time": T
}
feats.update(stats_block(d_seg, d_cols, "d_"))
feats.update(stats_block(r_seg, r_cols, "r_"))
return feats
analysis_train_sample_time = (analysis_train
.sort_values("sample_time")
.drop_duplicates(subset=["sample_time"], keep="last")
.reset_index(drop=True))
print("去重后行數(shù):", len(analysis_train_sample_time))
train_dicts = (analysis_train_sample_time.dropna(subset=["sample_time"])
.apply(lambda r: build_feature_row(
T=r["sample_time"],
distill_df=distillation_process_train,
react_df=reaction_process_train,
d_cols=d_cols, r_cols=r_cols,
WD=WD, OD=OD, WR=WR, OR=OR
), axis=1))train_feats = pd.DataFrame([d for d in train_dicts if d is not None]).sort_values("sample_time")特征構(gòu)建完成后,我們用Pandas把它變成一個(gè)DataFrame數(shù)據(jù)類型。然后,我們選擇哪些特征是我們需要的。這里我們主要把自己提取出來的特征,以及兩個(gè)ID類特征——sample(樣品)和component(組份)——都作為我們的特征。因?yàn)樵谔囟悠泛徒M份下,傳感器的狀態(tài)對(duì)y值有非常直接的影響,所以這兩個(gè)ID特征也非常重要。
train_df = analysis_train[["id","sample_time","sample","component","value"]].merge(train_feats, on="sample_time", how="inner")
# 拆出特征/標(biāo)簽
feature_cols = [c for c in train_df.columns if c.startswith(("d_","r_"))] + ["sample","component"]
X_train = train_df[feature_cols]
y_train = train_df["value"]以同樣的方式,我們對(duì)驗(yàn)證集也進(jìn)行同樣的處理,構(gòu)造出相同的特征。
analysis_validation_sample_time = (analysis_validation_without_truth
.sort_values("sample_time")
.drop_duplicates(subset=["sample_time"], keep="last")
.reset_index(drop=True))
print("去重后行數(shù):", len(analysis_validation_sample_time))
val_dicts = (analysis_validation_sample_time.dropna(subset=["sample_time"])
.apply(lambda r: build_feature_row(
T=r["sample_time"],
distill_df=distillation_process_validation,
react_df=reaction_process_validation,
d_cols=d_cols, r_cols=r_cols,
WD=WD, OD=OD, WR=WR, OR=OR
), axis=1))構(gòu)造完成后,接下來就是模型的訓(xùn)練。由于線上提交次數(shù)有限,大家可以先做一些線下的驗(yàn)證。這里我們用到了scikit-learn里的交叉驗(yàn)證函數(shù),進(jìn)行5折交叉驗(yàn)證,并計(jì)算出MAPE值。大家可以在增加更多特征后,先跑一下這個(gè)線下驗(yàn)證,看看效果有沒有提升。如果線下效果比較好,可能線上效果也會(huì)有提升。大家可以觀察一下線下交叉驗(yàn)證的結(jié)果和線上的結(jié)果是不是同增同減的。從目前來看,通過交叉驗(yàn)證得到的MAPE和線上的MAPE其實(shí)是有一點(diǎn)差距的。
cat_cols = ["sample","component"]
feature_cols = [c for c in train_df.columns if c.startswith(("d_","r_"))] + ["sample","component"]
X_train = X_train[feature_cols].copy() # 包含數(shù)值特征 + 這兩列
for c in cat_cols:
X_train[c] = X_train[c].astype("category") # 關(guān)鍵:設(shè)為分類 dtype
kf = KFold(n_splits=5, shuffle=True, random_state=42)
est = LGBMRegressor()
pred = cross_val_predict(est, X_train, y_train.astype(float), cv=kf, n_jobs=-1)
print("MAPE (ε=1e-3):", mean_absolute_percentage_error((y_train.astype(float)).clip(lower=1e-3),
pred.clip(min=1e-3)))如果想跑一個(gè)線上提交的版本,我們可以用全部訓(xùn)練數(shù)據(jù)訓(xùn)練一個(gè)LightGBM模型。因?yàn)槲覀兪穷A(yù)測(cè)數(shù)值,所以用LGBMRegressor。模型訓(xùn)練完成后,我們就可以對(duì)驗(yàn)證集進(jìn)行預(yù)測(cè),最后把結(jié)果保存成線上需要的格式,即一個(gè)包含ID和預(yù)測(cè)值(predict)的CSV文件,然后進(jìn)行提交。
X_val = X_val[feature_cols].copy() # 包含數(shù)值特征 + 這兩列
for c in cat_cols:
X_val[c] = X_val[c].astype("category") # 關(guān)鍵:設(shè)為分類 dtype
model = LGBMRegressor()
model.fit(X_train, y_train.astype(float), categorical_feature=cat_cols)
pred = model.predict(X_val)
pd.DataFrame({"id": val_df["id"], "predict": pred.astype(int)}).to_csv("result.csv", index=None)這個(gè)Baseline提交后,分?jǐn)?shù)大概是10點(diǎn)幾。因?yàn)槲覀儧]有用到很多特征,也沒有做很多優(yōu)化,所以這個(gè)分?jǐn)?shù)是一個(gè)非常正常的狀態(tài)。大家后面增加更多的特征之后,效果是可以達(dá)到一個(gè)比較好的水平的。
四、 優(yōu)化思路與總結(jié)
Baseline講完之后,我們來看一下這個(gè)賽題有哪些可以優(yōu)化的方向。
首先,在特征工程這塊,我們只用到了均值和標(biāo)準(zhǔn)差。大家其實(shí)可以增加一些其他的特征,比如我們之前提到的分位數(shù)、最大/最小值等,來更好地衡量區(qū)間內(nèi)的變化。這是第一個(gè)優(yōu)化點(diǎn)。
第二點(diǎn),我們剛才使用的窗口是一個(gè)固定的窗口。在不同的組份下,所依賴的窗口大小其實(shí)是不同的。如果大家沒有相關(guān)的業(yè)務(wù)知識(shí),可以怎么做呢?我們可以增加一個(gè)多尺度的窗口。例如,我們可以同時(shí)使用0-90分鐘、0-120分鐘、0-180分鐘等不同尺度的窗口來提取特征,衡量不同時(shí)間尺度下的變化。對(duì)于反應(yīng)窗口也是一樣的。
第三個(gè)更深入的優(yōu)化,是做一些交叉特征。比如,我們假設(shè)T開頭的特征是溫度,那么我們可以做一些特定位置的傳感器溫度減去另一個(gè)傳感器溫度的特征,來衡量它們之間的關(guān)系。大家可以自己線下探索這些特征的物理含義。一般來說,同一種前綴(如T, P, F, L, R, Z)的特征代表同一種物理量,我猜測(cè)它們是英文單詞的首字母。大家可以增加一些特征交叉的變化,因?yàn)椴煌恢蒙蟼鞲衅鞯那昂箨P(guān)系,比如溫度的變化,對(duì)于產(chǎn)品質(zhì)量是有非常大影響的。
第四個(gè)優(yōu)化點(diǎn)是在模型層面。我們使用的是默認(rèn)參數(shù)的LightGBM,大家可以測(cè)試一下不同模型參數(shù)的影響,進(jìn)行調(diào)參。
第五點(diǎn),可以做一些不同模型的融合。大家可以嘗試使用其他機(jī)器學(xué)習(xí)模型,甚至是一些時(shí)間序列模型,然后做模型融合。
此外,我們這里其實(shí)有很多特征,后面可以做一些特征篩選,因?yàn)橛行┨卣骺赡軟]有作用或者作用不是很大。比如,我們可以看到一些以R或Z開頭的特征,它們的取值可能是0或1,這可能代表了一些閥門的開關(guān)狀態(tài),大家需要去理解一下這些特征簡(jiǎn)單的物理含義。
最后,我們來簡(jiǎn)單總結(jié)一下本次賽題。
如果大家想在Baseline上做一些提升,首先要對(duì)數(shù)據(jù)和業(yè)務(wù)進(jìn)行理解,然后構(gòu)造有效的特征。
目標(biāo)明確:我們的X的ID是“樣品”和“組份”,預(yù)測(cè)目標(biāo)是“分析值”,評(píng)價(jià)指標(biāo)是MAPE。
特征構(gòu)造:大家要注意,構(gòu)造特征時(shí),比如采樣時(shí)間是早上7點(diǎn),我們只能用這個(gè)時(shí)間點(diǎn)之前的傳感器數(shù)據(jù)。所以,特征很明顯是基于一個(gè)時(shí)間窗口內(nèi)的數(shù)據(jù)變化來構(gòu)造的。
物理含義推斷:我們簡(jiǎn)單推理了T/P/F/L/R/Z可能分別代表溫度、壓力、流量、液位、閥位、狀態(tài),大家可以再驗(yàn)證一下。
特征類型:
基礎(chǔ)特征:我們只用到了平均值和方差,大家可以用更多特征,如最大值、最小值、極差以及一些平滑特征來構(gòu)建。
交叉特征:當(dāng)了解了關(guān)鍵信號(hào)后,可以構(gòu)建交叉特征,比如不同位置的溫度差、壓力變化等。
窗口選擇:我們用了固定窗口,大家可以嘗試對(duì)不同組份使用不同窗口,或者使用多尺度的窗口。
模型優(yōu)化:
模型選擇:可以探索不同的機(jī)器學(xué)習(xí)或深度學(xué)習(xí)模型。
模型融合:不同模型有不同的表示能力,可以做模型融合。
參數(shù)調(diào)優(yōu):可以對(duì)模型進(jìn)行調(diào)參。
驗(yàn)證策略:最后,結(jié)合交叉驗(yàn)證,對(duì)比線下和線上的效果,探索它們是否同增同減。
以上就是我們整體的賽題介紹、Baseline方案介紹,以及后續(xù)可能的一些優(yōu)化空間的分享。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.