阿里雲供應鏈大賽獨家baseline!


問題描述

名詞定義

  • 庫存水位:在倉庫存數量,用來滿足需求。
  • 補貨時長(交貨時間,lead_time)

從下達補貨指令到貨物到倉可用的時長。

本賽題初賽時的補貨時長為14天,即假設1號A貨物的庫存水位為0,此時下達A貨物補貨指令,補貨量為10,則1號至14號A貨物的庫存水位均為0,15號時A貨物的庫存水位為10。

  • 補貨在途

下達補貨指令后還未到倉的貨物量總和。

上例中1號至14號A貨物的補貨在途為10,其他時段為0。

若在8號再次下達補貨量為10的補貨指令,那么1號至7號的補貨在途為10,8號至14號的補貨在途為20,15號至21號的補貨在途為10,其他時段為0。

  • 補貨策略

本賽題場景使用周期性盤點的補貨策略,每周一為補貨決策日,決定貨物的補貨量。

  • 歷史需求

貨物歷史需求量的時間序列,值得注意的是,因為雲產品有購買與釋放的概念,所以本賽題場景下需求量會為負數,即雲產品被用戶釋放。(雲服務器釋放是什么意思?雲服務器釋放是指你購買的雲服務器產品到期未及時續費,而被雲服務商刪除數據,釋放該台機器資源的過程。)

  • 補貨單元

貨物的唯一標識。

賽題場景

基於給定過去一段時間的歷史需求數據,同時結合當前的庫存數據、補貨時長、補貨在途以及補貨單元的相關信息(產品維度與地理緯度),參賽者需要自己提出方案,在補貨決策日確定每一補貨單元的補貨量。最直接的方案,可通過歷史需求數據,對未來的需求進行預測,結合當前庫存水位以及補貨在途的貨物判斷14天后的庫存水位能否滿足14天后的一系列需求(因為當日補貨14天后才能到貨),考慮對應的補貨量,達到在保障一定服務水平的情況下,實現最低庫存成本的效果;當然也可采用end to end的整體優化方案,實現該目標。庫存量視角的變化過程如下圖所示。

 

我們會給一個訓練集,供參賽選手訓練模型並驗證模型效果使用。同時,初賽時我們會提供一個測試集,選手需要為按時間讀取20210302-20210607的需求數據,並根據歷史需求數據以及補貨在途,決定補貨量,並把決策結果CSV文件輸出到指定位置。(由於賽題特殊與評價機制的問題,初賽時給選手透出了未來數據,選手在初賽時將其作為未知數據調整算法,不允許在決策時使用當前日之后的未來數據。)

由於庫存控制是一個前后相關的決策過程,兩次補貨決策並不獨立,因此我們會提供一段時間的數據,由選手在時間軸上進行多次補貨決策,最后在較長的時間段內評價選手方案的好壞。在復賽時,選手需要提交一個docker鏡像,鏡像中需要包含用來進行庫存管理所需的所有內容,如模型、腳本等,未來的需求數據以及到貨時間將通過流評測的方式給出。鏡像中的腳本需要能根據所得的需求量,根據歷史需求數據以及先前所做的決策,決定補貨量,並把決策結果CSV文件輸出到指定位置;初賽時測試集文件將線下交給選手。

相關參考

https://web.mit.eu/2.810/www/files/readings/King_SafetyStock.pdf

https://abcsupplychain.com/safety-stock-formula-calculation/

數據描述

訓練集包含如下信息:

虛擬資源使用量歷史數據

demand_train.csv為虛擬資源使用量歷史數據,包含7列。每列的含義如下:

字段

說明

unit

單元

ts

日期

qty

資源使用量

geography

地理信息

geography_level

地理聚合維度

product

產品信息

product_level

產品聚合維度

inventory_info.csv為虛擬資源庫存數據,包含7列。每列的含義如下:

字段

說明

unit

單元

ts

日期

qty

庫存量

geography

地理信息

geography_level

地理聚合維度

product

產品信息

product_level

產品聚合維度

geography_tuopu.csv為地理拓撲數據,包含3列。每列的含義如下:

字段

說明

geography_level_1

地理層級1

geography_level_2

地理層級2

geography_level_3

地理層級3

product_tuopu.csv為產品層級信息,包含2列。每列的含義如下:

字段

說明

product_level_1

產品層級1

product_level_2

產品層級2

unit_weight.csv為庫存單元的庫存信息,包含2列。每列的含義如下:

字段

 

說明

unit

 

單元

weight

 

權重

復賽階段,測試集的數據格式和初賽階段相同,但是測試集的內容不會提供給參賽選手。選手需要在docker代碼中從指定的數據集目錄中讀取測試集內容,進行特征工程和模型預測,最后輸出的格式和初賽一致,輸出后續一段時間內決策日需要補貨的集合(如果有預訓練權重則docker代碼中需包含本地訓練好的模型)。

提交格式

在初賽中,預測結果的提交格式為CSV壓縮的ZIP文件。文件中的每一行表示在未來的時間段內每個決策日每個庫存單元需要補充的量,具體包含:補貨單元、日期和補貨量(用逗號隔開),形式如下:

unit,ts,qty

"unit1","20210308",12

"unit1","20210315",11

"unit1","20210322",9

注:在復賽中,參賽選手需要提交docker鏡像,具體的提交方式及規范請參見到時發布的容器鏡像提交說明。

評價指標

綜合指標

 

 

另外

評測時按時間順序更新上述變量,完畢后用上述公式計算指標。

#初賽評測程序偽代碼如下

#intransit 是用來記錄預計到達的資源的中間變量

init intransit[][] = 0

for i in I:

    for d in D:

        #初始庫存值已給出

        qty_inventory[i,d] = qty_inventory[i,d - 1]

        #qty_replenish即為選手提交的補貨決策

        if qty_replenish[i,d] > 0:

            #lead_time 為交貨時間,在交貨時間之后供應會到達

            intransit[i,d + lead_time] += qty_replenish[i,d]

        # 可用庫存加上到達的庫存

        qty_inventory[i,d] = max(qty_inventory[i,d] + intransit[i,d], 0)

       

        # 需求沒有被滿足的量,當天需求量和可用庫存量進行比較

        stockout[i,d] = max(demand[i,d] - qty_inventory[i,d], 0)

        if demand[i,d] < 0:

            # 凈需求為負時候,表示釋放,可用庫存會增加,釋放的量和已使用的量取小

            qty_inventory[i,d] = max(qty_inventory[i,d] + min(abs(demand[i,d]),qty_using[i,d - 1]), 0)

        else:

            # 凈需求為正的時候,可用庫存進行扣減,>=0

            qty_inventory[i,d] = max(qty_inventory[i,d] - demand[i,d], 0)

        # 前一天的保有量+今天被滿足的量

        qty_using[i,d] = max(qty_using[i,d - 1] + demand[i,d] - stockout[i,d], 0)

整體指標平衡了庫存率和服務水平,達到了一定服務水平的情況下,庫存率就會更重要。

賽題Baseline

數據准備

import os
import pandas as pd

demand_train_A = 'data/demand_train_A.csv'
geo_topo = 'data/geo_topo.csv'
inventory_info_A = 'data/inventory_info_A.csv'
product_topo = 'data/product_topo.csv'
weight_A = 'data/weight_A.csv'

demand_train_A = pd.read_csv(demand_train_A)
geo_topo = pd.read_csv(geo_topo)
inventory_info_A = pd.read_csv(inventory_info_A)
product_topo = pd.read_csv(product_topo)
weight_A = pd.read_csv(weight_A)

demand_test_A = 'data/demand_test_A.csv'

demand_test_A = pd.read_csv(demand_test_A)

dfs = [demand_train_A,geo_topo,inventory_info_A,product_topo,weight_A,demand_test_A]
for df in dfs:
    if 'Unnamed: 0' in df.columns:
        df.drop(columns='Unnamed: 0',inplace=True)
    if 'ts' in df.columns:
        df = df.sort_values(by='ts')

數據拼接

all_data = pd.concat([demand_train_A,demand_test_A])
all_data = all_data.sort_values(by='ts')
all_data = all_data.reset_index().drop(columns='index')

預測未來銷量

## 真實值串14天
submission = demand_test_A
submission['yesterday_qty'] = submission.groupby('unit')['qty'].shift(1).fillna(method='ffill').reset_index().sort_index().set_index('index')

submission['diff_1'] = submission['qty'] - submission['yesterday_qty']
submission['qty'] = submission['diff_1']

submission['shift_14']=submission.groupby('unit')['qty'].shift(-14).fillna(0).reset_index().sort_index().set_index('index')
submission = submission[['unit','ts','shift_14']].rename(columns={'shift_14':'qty'})

# 按照7天聚合
submission['dt'] = pd.to_datetime(submission['ts'])
submission['weekofyear'] = submission['dt'].dt.weekofyear
submission['year'] = submission['dt'].dt.year
submission_week = submission.copy()
submission_week = submission_week.groupby(['weekofyear','year','unit'],as_index=False).sum()
submission_week['sum_qty'] = submission_week['qty']
submission = pd.merge(submission_week,submission,on = ['weekofyear','year','unit'])
submission['dayofweek'] = submission['dt'].dt.dayofweek
submission = submission[submission['dayofweek']==0]
submission = submission[['unit','ts','sum_qty']].rename(columns={'sum_qty':'qty'})

根據未來需求消耗掉初始庫存

init_inventory = inventory_info_A.set_index(['unit'])['qty'].to_dict()
def consume_init_inventory(arr,init_val):
    remain = init_val
    i = 0
    while remain>0 and i<len(arr):
        arr[i] = max(0,arr[i]-remain)
        remain -= arr[i]
        i+=1
    return arr

r = []
for i,group in submission.groupby('unit'):

    unit = group['unit'].values[0]
    init_val = init_inventory[unit]
    
    group = group.sort_values(by='ts')
    qty_list = group['qty'].values
    qty_list = consume_init_inventory(qty_list,init_val)
    group['qty'] = qty_list
    r.append(group)

submission = pd.concat(r)      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM