Nghiên cứu dữ liệu
Trong thực tế, Walmart đã chạy các chương trình khuyến mãi trong các ngày lễ lớn trong năm. Có 4 ngày lễ lớn đó là Siêu cúp bóng bầu dục Mỹ (Super Bowl - tổ chức vào chủ nhật đầu tiên của tháng Hai. Đây là một sự kiện thể thao lớn và ngày tổ chức Super Bowl được người Mỹ coi là ngày lễ quốc gia của Hoa Kỳ (theo wiki https://vi.wikipedia.org/wiki/Super_Bowl)), ngày lễ lao động (Labor Day - ngày một tháng 5), lễ tạ ơn (Thanksgiving, ngày lễ tạ ơn ở Mỹ được tổ chức vào ngày thứ Năm lần thứ tư của tháng 11, còn ở Canada ngày lễ tạ ơn được tổ chức vào ngày thứ hai lần thứ hai của tháng 10, theo wiki https://en.wikipedia.org/wiki/Thanksgiving), lễ giáng sinh (Christmas ngày 24 và 25 tháng 12 theo wiki https://en.wikipedia.org/wiki/Christmas ). Những tuần có chứa những ngày lễ lớn này được đánh trọng số gấp 5 lần những tuần khác. Chúng ta phải xây dựng mô hình để mô hình hoá các tác động của việc giảm giá trong các tuần lễ này khi không có dữ liệu lịch sử đầy đủ.
Tập dữ liệu được cung cấp bao gồm:
Tập train: chứa dữ liệu số bán từ 05-02-2010 đến 01-11-2012. Các trường dữ liệu là: store number - mã cửa hàng, Dept number - mã sản phẩm, Date - Tuần, Weekly_Sales - số bán, IsHoliday - Nếu tuần đó có chứa các holidate thì đánh 1 ngược lại đánh 0.
Tập test: Chứa dữ liệu test, có các cột thuộc tính như tập train
Tập features: Chứa thông tin thêm về của hàng, bao gồm store - mã cửa hàng, Date - ngày, Temperature - Nhiệt độ, Fuel_Price - giá dầu (ở mỹ, mỗi khu vực khác nhau sẽ có giá nhiên liệu khác nhau), MarkDown1, MarkDown2,… , MarkDown5 - một chỉ số gì đó mà tác giả không cung cấp định nghĩa cho chúng ta, CPI - chỉ số giá tiêu dùng, Unemployment - tình trạng thất nghiệp, IsHoliday - Tuần có chứa ngày nghỉ.
Phân tích dữ liệu
Mình sẽ import một số thư viện cần thiết
1import pandas as pd
2import numpy as np
3
4#Do some statistics
5from scipy.misc import imread
6from scipy import sparse
7import scipy.stats as ss
8import math
9
10#Nice graphing tools
11import matplotlib
12import matplotlib.pyplot as plt
13import seaborn as sns
Đọc các file data lên, merge các file lại với nhau
1
2
3train = pd.read_csv('data/train.csv')
4test = pd.read_csv('data/test.csv')
5feature = pd.read_csv('data/features.csv')
6
7train = train.merge(feature, how='left', on=['Store','Date'])
8test = test.merge(feature, how='left', on=['Store','Date'])
9
10
11# Merge in store info
12stores = pd.read_csv("data/stores.csv")
13train = train.merge(stores, how='left', on='Store')
14test = test.merge(stores, how='left', on='Store')
15print(train.head())
Kết quả
1 Store Dept Date Weekly_Sales IsHoliday_x Temperature Fuel_Price MarkDown1 MarkDown2 MarkDown3 MarkDown4 MarkDown5 CPI Unemployment IsHoliday_y Type Size Split
20 1 1 2010-02-05 24924.50 False 42.31 2.572 NaN NaN NaN NaN NaN 211.096358 8.106 False A 151315 Train
31 1 1 2010-02-12 46039.49 True 38.51 2.548 NaN NaN NaN NaN NaN 211.242170 8.106 True A 151315 Train
42 1 1 2010-02-19 41595.55 False 39.93 2.514 NaN NaN NaN NaN NaN 211.289143 8.106 False A 151315 Train
53 1 1 2010-02-26 19403.54 False 46.63 2.561 NaN NaN NaN NaN NaN 211.319643 8.106 False A 151315 Train
64 1 1 2010-03-05 21827.90 False 46.50 2.625 NaN NaN NaN NaN NaN 211.350143 8.106 False A 151315 Train
Mới có 5 dòng đầu tiên mà thấy các chỉ số markdown Nan rồi.
Chúng ta tiến hành một số phân tích dữ liệu. À, Mình sẽ merge dữ liệu train và test lại rồi phân tích thống kê
1df = pd.concat([train,test],axis=0) # Join train and test
2
3print(df.describe())
Kết quả
1 CPI Dept Fuel_Price MarkDown1 MarkDown2 MarkDown3 MarkDown4 MarkDown5 Size Store Temperature Unemployment Weekly_Sales
2count 498472.000000 536634.000000 536634.000000 265596.000000 197685.000000 242326.000000 237143.000000 266496.000000 536634.00000 536634.000000 536634.000000 498472.000000 421570.000000
3mean 172.090481 44.277301 3.408310 7438.004144 3509.274827 1857.913525 3371.556866 4324.021158 136678.55096 22.208621 58.771762 7.791888 15981.258123
4std 39.542149 30.527358 0.430861 9411.341379 8992.047197 11616.143274 6872.281734 13549.262124 61007.71180 12.790580 18.678716 1.865076 22711.183519
5min 126.064000 1.000000 2.472000 -2781.450000 -265.760000 -179.260000 0.220000 -185.170000 34875.00000 1.000000 -7.290000 3.684000 -4988.940000
625% 132.521867 18.000000 3.041000 2114.640000 72.500000 7.220000 336.240000 1570.112500 93638.00000 11.000000 45.250000 6.623000 2079.650000
750% 182.442420 37.000000 3.523000 5126.540000 385.310000 40.760000 1239.040000 2870.910000 140167.00000 22.000000 60.060000 7.795000 7612.030000
875% 213.748126 74.000000 3.744000 9303.850000 2392.390000 174.260000 3397.080000 5012.220000 202505.00000 33.000000 73.230000 8.549000 20205.852500
9max 228.976456 99.000000 4.468000 103184.980000 104519.540000 149483.310000 67474.850000 771448.100000 219622.00000 45.000000 101.950000 14.313000 693099.360000
Phân tích một chút:
Bỏ qua cột Dept và Store vì nó là mã sản phẩm và mã của hàng, người ta thích đặt số bao nhiêu thì đặt.
Các chỉ số MarkDown có độ lệch chuẩn khá cao.
Nhiệt độ min là -7.29, max là 101.95, trung bình là 58, nên không thể là độ C được, có thể là độ F
Xem thử hệ số tương quan giữa các column như thế nào
1sns.set(style="white")
2
3# Compute the correlation matrix
4corr = df.corr()
5
6# Generate a mask for the upper triangle
7mask = np.zeros_like(corr, dtype=np.bool)
8mask[np.triu_indices_from(mask)] = True
9
10# Set up the matplotlib figure
11f, ax = plt.subplots(figsize=(11, 9))
12
13# Generate a custom diverging colormap
14cmap = sns.diverging_palette(220, 10, as_cmap=True)
15
16# Draw the heatmap with the mask and correct aspect ratio
17sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, center=0,
18 square=True, linewidths=.5, cbar_kws={"shrink": .5})
19
20plt.show()
Hệ số tương quan giữa các cột trong dữ liệu
Phân tích một chút, chúng ta thấy rằng MarkDown5 hầu như không có liên quan gì đến các column còn lại. Hệ số trải từ -0.3 đến 0.3 chứng tỏ mổi quan hệ giữa các cột là khá lỏng lẻo. Chỉ số giá tiêu dùng tương quan tỷ lệ nghịch với tình trạng thất nghiệp (hợp lý không nhỉ). Kích thước cửa hàng càng bự thì bán càng nhiều (ok hiển nhiên), sản phẩm có mã càng lớn thì bán càng nhiều (? có lẽ là sản phẩm mới, người mỹ thích mua sản phẩm mới chăng). Và một vấn đề quan trọng là giá nhiên liệu, isHoliday, nhiệt độ không có mối tương quan với weekly sales. Chỉ số CPI và tình trạng thất nghiệp cũng ảnh hưởng không lớn với weekly sales.
Thử plot lên hình ảnh về số lượng bán và kích thước cửa hàng xem sao
1plt.scatter( df['Size'],df['Weekly_Sales'])
2plt.show()
Tương quan giữa số bán và kích thước cửa hàng
Nhìn vào hình trên, chúng ta thấy rằng cửa hàng có kích thước nhỏ số bán cũng không tăng đột biến khi gặp ngày lễ, cửa hàng kích thước siêu bự có tỷ lệ đột biến thấp, cửa hàng trung trung có đột biến, ở khúc size 125000 và số bán là 700000. Chúng ta hãy xem những ngày có số bán lớn rơi vào ngày nào. Dựa vào bảng desription ở phía trên đã phân tích, trung bình của số bán là 15981 và lệch chuẩn là 22711, cộng lại là 15981 + 22711 = 38692, nhìn trên đô thị thì phần đột biến khá lớn. Max là 700000, min là 0 (cái này nhìn hình, không phải số thực tế ở bảng mô tả), mình sẽ lấy ra những ngày có số bán lớn hơn 350000 (vượt qua ngưỡng trung bình + độ lệch chuẩn rất nhều -> ngoại lệ là đây) xem những ngày đó là ngày gì
1
2print(df.loc[df['Weekly_Sales'] >350000].head(10))
In ra top 10 thằng đầu tiên
1
2 CPI Date Dept Fuel_Price IsHoliday_x IsHoliday_y MarkDown1 MarkDown2 MarkDown3 MarkDown4 MarkDown5 Size Split Store Temperature Type Unemployment Weekly_Sales
337201 126.669267 2010-11-26 72 2.752 True True NaN NaN NaN NaN NaN 205863 Train 4 48.08 A 7.127 381072.11
437253 129.836400 2011-11-25 72 3.225 True True 561.45 137.88 83340.33 44.04 9239.23 205863 Train 4 47.96 A 5.143 385051.04
588428 126.983581 2010-12-24 7 3.236 False False NaN NaN NaN NaN NaN 126512 Train 10 57.06 B 9.003 406988.63
695373 126.669267 2010-11-26 72 3.162 True True NaN NaN NaN NaN NaN 126512 Train 10 55.33 B 9.003 693099.36
795377 126.983581 2010-12-24 72 3.236 False False NaN NaN NaN NaN NaN 126512 Train 10 57.06 B 9.003 404245.03
895425 129.836400 2011-11-25 72 3.760 True True 174.72 329.00 141630.61 79.00 1009.98 126512 Train 10 60.68 B 7.874 630999.19
9115222 126.669267 2010-11-26 72 3.162 True True NaN NaN NaN NaN NaN 112238 Train 12 47.66 B 14.313 359995.60
10115274 129.836400 2011-11-25 72 3.622 True True 5391.83 8.00 63143.29 49.27 2115.67 112238 Train 12 53.25 B 12.890 360140.66
11128984 182.544590 2010-12-24 7 3.141 False False NaN NaN NaN NaN NaN 200898 Train 14 30.59 A 8.724 356867.25
12135665 182.783277 2010-11-26 72 3.039 True True NaN NaN NaN NaN NaN 200898 Train 14 46.15 A 8.724 474330.10
Nhìn vào bảng trên, chúng ta thấy rằng 10 ngày đầu tiên tập trung chủ yếu ở tháng 11 và tháng 12, tháng 12 là 24-25 tháng 12 -> ngày noel, còn tháng 11 là 25-26 tháng 11 (ngày gì vậy ta, trong mô tả không thấy) Tra lịch thì ngày 25 tháng 11 năm 2011 trúng thứ sáu, tra trên mạng một thông tin khá quan trong là “Black Friday sẽ rơi vào khoảng ngày 23-29 tháng 11” -> không nghi ngờ gì nữa có thể là ngày này đây. Thử tra tiếp ngày 26 tháng 11 năm 2010, cũng là thứ sáu luôn -> ngày black friday và ngày noel có sức mua điên cuồng quá.
Mình dùng một kỹ thuật nhỏ là giảm dần số bán, để ra số bán tối thiểu mà ngày black friday và ngày nodel vẫn còn giữ vị trí thống trị. Kỹ thuật khá đơn giản thôi, từ giá trị 350000, mỗi lần mình sẽ giảm đi 10000, và đếm số lần xuất hiện của các ngày, nếu có ngày nào đó nằm ngoài tuần chứa black friday và nodel thì mình dừng. Sau một hồi tìm kiếm và số bán đã xuất hiện, đó là 290000
1print(df.loc[df['Weekly_Sales'] >290000,"Date"].value_counts())
12010-11-26 16
22011-11-25 14
32010-12-24 8
42011-12-23 3
52010-02-05 1
Làm sạch dữ liệu
Xử lý missing values
Một vấn đề khá quan trọng là trong tập dữ liệu này missing value khá nhiều, thử đếm số lượng null trong data cho ta biết được rằng
1CPI 38162
2Date 0
3Dept 0
4Fuel_Price 0
5IsHoliday_x 0
6IsHoliday_y 0
7MarkDown1 271038
8MarkDown2 338949
9MarkDown3 294308
10MarkDown4 299491
11MarkDown5 270138
12Size 0
13Split 0
14Store 0
15Temperature 0
16Type 0
17Unemployment 38162
18Weekly_Sales 115064
Các giá trị MarkDown bị null khá nhiều, cách đơn giản nhất là set 0 cho tất cả các giá trị null ( Mình lưu log lại những index null của các markdown).
1df = df.assign(md1_present = df['MarkDown1']notnull())
2df = df.assign(md2_present = df['MarkDown2']notnull())
3df = df.assign(md3_present = df['MarkDown3']notnull())
4df = df.assign(md4_present = df['MarkDown4']notnull())
5df = df.assign(md5_present = df['MarkDown5'].notnull())
6
7df.fillna(0, inplace=True)
Tạo đặc trưng
Đặc trưng holiday
1df['IsHoliday'] = 'IsHoliday_' + df['IsHoliday_x'].map(str)
2holiday_dummies = pd.get_dummies(df['IsHoliday'])
Đặc trưng ngày tháng
Rút trích tháng
1df['DateType'] = [datetime.strptime(date, '%Y-%m-%d').date() for date in df['Date'].astype(str).values.tolist()]
2df['Month'] = [date.month for date in df['DateType']]
3df['Month'] = 'Month_' + df['Month'].map(str)
4Month_dummies = pd.get_dummies(df['Month'] )
Rút trích ngày trước giáng sinh và black friday
1df['Black_Friday'] = np.where((df['DateType']==datetime(2010, 11, 26).date()) | (df['DateType']==datetime(2011, 11, 25).date()), 'yes', 'no')
2df['Pre_christmas'] = np.where((df['DateType']==datetime(2010, 12, 23).date()) | (df['DateType']==datetime(2010, 12, 24).date()) | (df['DateType']==datetime(2011, 12, 23).date()) | (df['DateType']==datetime(2011, 12, 24).date()), 'yes', 'no')
3df['Black_Friday'] = 'Black_Friday_' + df['Black_Friday'].map(str)
4df['Pre_christmas'] = 'Pre_christmas_' + df['Pre_christmas'].map(str)
5Black_Friday_dummies = pd.get_dummies(df['Black_Friday'] )
6Pre_christmas_dummies = pd.get_dummies(df['Pre_christmas'] )
Thêm các đặc trưng vào trong dữ liệu
1
2df = pd.concat([df,holiday_dummies,Pre_christmas_dummies,Black_Friday_dummies],axis=1)
Thêm đặc trưng trung vị của từng loại cửa hàng vào từng tháng, do một số của hàng sẽ bị NA ở cột số bán ở một thời điểm nào đó, nên chúng ta replace số bán là 0 có vẻ không hợp lý lắm. Mình chọn cách là thay thế bằng trung bình của số bán trong tháng của cửa hàng cùng loại. Nhưng trước tiên thì tính trung bình số bán của từng loại cửa hàng cái đã.
1
2medians = pd.DataFrame({'Median Sales' :df.loc[df['Split']=='Train'].groupby(by=['Type','Dept','Store','Month','IsHoliday'])['Weekly_Sales'].median()}).reset_index()
3print(medians.head())
Kết quả
1 Type Dept Store Month IsHoliday Median Sales
20 Type_A Dept_1 Store_1 Month_1 IsHoliday_False 17350.585
31 Type_A Dept_1 Store_1 Month_10 IsHoliday_False 23388.030
42 Type_A Dept_1 Store_1 Month_11 IsHoliday_False 19551.115
53 Type_A Dept_1 Store_1 Month_11 IsHoliday_True 19865.770
64 Type_A Dept_1 Store_1 Month_12 IsHoliday_False 39109.390
thêm dữ liệu vào trong data chính, loại bỏ NA và tạo key cho mỗi dòng để dễ dàng truy xuất
1df = df.merge(medians, how = 'outer', on = ['Type','Dept','Store','Month','IsHoliday'])
2
3# Fill NA
4df['Median Sales'].fillna(df['Median Sales'].loc[df['Split']=='Train'].median(), inplace=True)
5
6# Create a key for easy access
7
8df['Key'] = df['Type'].map(str)+df['Dept'].map(str)+df['Store'].map(str)+df['Date'].map(str)+df['IsHoliday'].map(str)
Chúng ta sẽ dự đoán số bán của tuần kế tiếp dựa vào kết quả số bán của tuần hiện tại, nên trong dữ liệu sẽ lưu trên ngày của tuần trước đó để dễ truy xuất. Vì 1 tuần có 7 ngày, chúng ta sẽ lưu giá trị là ngày ở cột hiện tại - 7
1df['DateLagged'] = df['DateType']- timedelta(days=7)
Và giờ đây, chúng ta sẽ lặp qua toàn bộ các dòng trên tập dữ liệu, kiểm tra xem có dòng nào số bán nan hông, nếu có thì sẽ thay bằng trung bình đã tính ở trên. Ở đây mình tạo một sorted dataset để truy xuất cho nhanh
1
2#Make a sorted dataframe. This will allow us to find lagged variables much faster!
3sorted_df = df.sort_values(['Store', 'Dept','DateType'], ascending=[1, 1,1])
4sorted_df = sorted_df.reset_index(drop=True) # Reinitialize the row indices for the loop to work
5
6sorted_df['LaggedSales'] = np.nan # Initialize column
7sorted_df['LaggedAvailable'] = np.nan # Initialize column
8last=df.loc[0] # intialize last row for first iteration. Doesn't really matter what it is
9row_len = sorted_df.shape[0]
10for index, row in sorted_df.iterrows():
11 lag_date = row["DateLagged"]
12 # Check if it matches by comparing last weeks value to the compared date
13 # And if weekly sales aren't 0
14 if((last['DateType']== lag_date) & (last['Weekly_Sales']>0)):
15 sorted_df.set_value(index, 'LaggedSales',last['Weekly_Sales'])
16 sorted_df.set_value(index, 'LaggedAvailable',1)
17 else:
18 sorted_df.set_value(index, 'LaggedSales',row['Median Sales']) # Fill with median
19 sorted_df.set_value(index, 'LaggedAvailable',0)
20
21 last = row #Remember last row for speed
22 if(index%int(row_len/10)==0): #See progress by printing every 10% interval
23 print(str(int(index*100/row_len))+'% loaded')
24
25print(sorted_df[['Dept', 'Store','DateType','LaggedSales','Weekly_Sales','Median Sales']].head())
19% loaded
219% loaded
329% loaded
439% loaded
549% loaded
659% loaded
769% loaded
879% loaded
989% loaded
1099% loaded
11 Dept Store DateType LaggedSales Weekly_Sales Median Sales
120 Dept_1 Store_1 2010-02-05 23510.49 24924.50 23510.49
131 Dept_1 Store_1 2010-02-12 24924.50 46039.49 37887.17
142 Dept_1 Store_1 2010-02-19 46039.49 41595.55 23510.49
153 Dept_1 Store_1 2010-02-26 41595.55 19403.54 23510.49
164 Dept_1 Store_1 2010-03-05 19403.54 21827.90 21280.40
Công việc đơn giản tiếp theo là merge dữ liệu vào data chính và tính độ lệch giữa 2 tuần bán
1# Merge by store and department
2df = df.merge(sorted_df[['Dept', 'Store','DateType','LaggedSales','LaggedAvailable']], how = 'inner', on = ['Dept', 'Store','DateType'])
3df['Sales_dif'] = df['Median Sales'] - df['LaggedSales']
Và bây giờ , thay vì ta ước lượng weekly sales, chúng ta sẽ ước lượng độ lệch giữa week sales và median sales (đây là một cách trong những cách để tính điểm dừng của dữ liệu time series)
1df['Difference'] = df['Median Sales'] - df['Weekly_Sales']
Huấn luyện mô hình
Lựa chọn các đặc trưng huấn luyện
1selector = [
2 #'Month',
3 'CPI',
4 'Fuel_Price',
5 'MarkDown1',
6 'MarkDown2',
7 'MarkDown3',
8 'MarkDown4',
9 'MarkDown5',
10 'Size',
11 'Temperature',
12 'Unemployment',
13
14
15
16 'md1_present',
17 'md2_present',
18 'md3_present',
19 'md4_present',
20 'md5_present',
21
22 'IsHoliday_False',
23 'IsHoliday_True',
24 'Pre_christmas_no',
25 'Pre_christmas_yes',
26 'Black_Friday_no',
27 'Black_Friday_yes',
28 'LaggedSales',
29 'Sales_dif',
30 'LaggedAvailable'
31 ]
Tách dữ liệu train và test riêng ra
1
2train = df.loc[df['Split']=='Train']
3test = df.loc[df['Split']=='Test']
Lấy ngẫu nhiên 20% dữ liệu ở tập train để validation
1# Set seed for reproducability
2np.random.seed(42)
3X_train, X_val, y_train, y_val = train_test_split(train[selector], train['Difference'], test_size=0.2, random_state=42)
Huấn luyện bằng neural network sử dụng lstm
1
2adam_regularized = Sequential()
3
4 # First hidden layer now regularized
5 model.add(Dense(32,activation='relu',
6 input_dim=X_train.shape[1],
7 kernel_regularizer = regularizers.l2(0.01)))
8
9 # Second hidden layer now regularized
10 adam_regularized.add(Dense(16,activation='relu',
11 kernel_regularizer = regularizers.l2(0.01)))
12
13 # Output layer stayed sigmoid
14 adam_regularized.add(Dense(1,activation='linear'))
15
16 # Setup adam optimizer
17 adam_optimizer=keras.optimizers.Adam(lr=0.01,
18 beta_1=0.9,
19 beta_2=0.999,
20 epsilon=1e-08)
21
22 # Compile the model
23 adam_regularized.compile(optimizer=adam_optimizer,
24 loss='mean_absolute_error',
25 metrics=['acc'])
26
27 # Train
28 history=adam_regularized.fit(X_train, y_train, # Train on training set
29 epochs=10, # We will train over 1,000 epochs
30 batch_size=2048, # Batch size
31 verbose=0) # Suppress Keras output
32 print('eval',model.evaluate(x=X_val,y=y_val))
33
34 # Plot network
35 plt.plot(history.history['loss'], label='Adam Regularized')
36 plt.xlabel('Epochs')
37 plt.ylabel('loss')
38 plt.legend()
39 plt.show()
1eval: [1457.0501796214685, 0.002312783168124545]
Độ lỗi trên tập train
Độ lỗi trên tập train giảm xuống đến gần 1450 thì đừng hẳn, không thể giảm được nữa
Giá trị độ lệch trên tập evaluation là 1457.0501796214685
Thử huấn luyện bằng random forest
1regr = RandomForestRegressor(n_estimators=20, criterion='mse', max_depth=None,
2 min_samples_split=2, min_samples_leaf=1,
3 min_weight_fraction_leaf=0.0, max_features='auto',
4 max_leaf_nodes=None, min_impurity_decrease=0.0,
5 min_impurity_split=None, bootstrap=True,
6 oob_score=False, n_jobs=1, random_state=None,
7 verbose=2, warm_start=False)
8
9 #Train on data
10 regr.fit(X_train, y_train.ravel())
11 y_pred_random = regr.predict(X_val)
12
13 y_val = y_val.to_frame()
14
15 # Transform forest predictions to observe direction of change
16 direction_true1= y_val.values
17 direction_predict = y_pred_random
18
19 y_val['Predicted'] = y_pred_random
20 df_out = pd.merge(train,y_val[['Predicted']],how = 'left',left_index = True, right_index = True,suffixes=['_True','_Pred'])
21 df_out = df_out[~pd.isnull(df_out['Predicted'])]
22
23 df_out['prediction'] = df_out['Median Sales']-df_out['Predicted']
24
25 print("Medians: "+str(sum(abs(df_out['Difference']))/df_out.shape[0]))
26 print("Random Forest: "+str(sum(abs(df_out['Weekly_Sales']-df_out['prediction']))/df_out.shape[0]))
Kết quả
1
29% loaded
319% loaded
429% loaded
539% loaded
649% loaded
759% loaded
869% loaded
979% loaded
1089% loaded
1199% loaded
12 Dept Store DateType LaggedSales Weekly_Sales Median Sales
130 Dept_1 Store_1 2010-02-05 23510.49 24924.50 23510.49
141 Dept_1 Store_1 2010-02-12 24924.50 46039.49 37887.17
152 Dept_1 Store_1 2010-02-19 46039.49 41595.55 23510.49
163 Dept_1 Store_1 2010-02-26 41595.55 19403.54 23510.49
174 Dept_1 Store_1 2010-03-05 19403.54 21827.90 21280.40
18[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
19building tree 1 of 20
20[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 6.5s remaining: 0.0s
21building tree 2 of 20
22building tree 3 of 20
23building tree 4 of 20
24building tree 5 of 20
25building tree 6 of 20
26building tree 7 of 20
27building tree 8 of 20
28building tree 9 of 20
29building tree 10 of 20
30building tree 11 of 20
31building tree 12 of 20
32building tree 13 of 20
33building tree 14 of 20
34building tree 15 of 20
35building tree 16 of 20
36building tree 17 of 20
37building tree 18 of 20
38building tree 19 of 20
39building tree 20 of 20
40[Parallel(n_jobs=1)]: Done 20 out of 20 | elapsed: 2.2min finished
41[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
42[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 0.0s remaining: 0.0s
43[Parallel(n_jobs=1)]: Done 20 out of 20 | elapsed: 1.1s finished
44Medians: 1545.7406070759525
45Random Forest: 1356.4670052620745
Trung bình lệch của random forest là 1356, giá trị này nhỏ hơn so với giá trị output của lstm trả về.
Thử huấn luyện bằng XGBoost
1
2param_dist = { 'max_depth':5}
3
4 model = XGBRegressor(**param_dist)
5
6 #Train on data
7 model.fit(X_train, y_train.ravel())
8 y_pred_random = model.predict(X_val)
9
10 y_val = y_val.to_frame()
11
12 # Transform forest predictions to observe direction of change
13 direction_true1= y_val.values
14 direction_predict = y_pred_random
15
16 y_val['Predicted'] = y_pred_random
17 df_out = pd.merge(train,y_val[['Predicted']],how = 'left',left_index = True, right_index = True,suffixes=['_True','_Pred'])
18 df_out = df_out[~pd.isnull(df_out['Predicted'])]
19
20 df_out['prediction'] = df_out['Median Sales']-df_out['Predicted']
21
22 print("Medians: "+str(sum(abs(df_out['Difference']))/df_out.shape[0]))
23 print("XGB Regressor: "+str(sum(abs(df_out['Weekly_Sales']-df_out['prediction']))/df_out.shape[0]))
Kết quả
1
2Medians: 1545.7406070759525
3XGB Regressor: 1354.1976755192593
Kết quả cũng gần như bằng Random forest :).
Giờ mình sẽ dùng random forest để tạo file submission
1
2
3rf_model = RandomForestRegressor(n_estimators=80, criterion='mse', max_depth=None,
4 min_samples_split=2, min_samples_leaf=1,
5 min_weight_fraction_leaf=0.0, max_features='auto',
6 max_leaf_nodes=None, min_impurity_decrease=0.0,
7 min_impurity_split=None, bootstrap=True,
8 oob_score=False, n_jobs=1, random_state=None,
9 verbose=0, warm_start=False)
10
11#Train on data
12rf_model.fit(train[selector], train['Difference'])
13final_y_prediction = rf_model.predict(test[selector])
14
15testfile = pd.concat([test.reset_index(drop=True), pd.DataFrame(final_y_prediction)], axis=1)
16testfile['prediction'] = testfile['Median Sales']-testfile[0]
17
18submission = pd.DataFrame({'id':pd.Series([''.join(list(filter(str.isdigit, x))) for x in testfile['Store']]).map(str) + '_' +
19 pd.Series([''.join(list(filter(str.isdigit, x))) for x in testfile['Dept']]).map(str) + '_' +
20 testfile['Date'].map(str),
21 'Weekly_Sales':testfile['prediction']})
22
23submission.to_csv('submission.csv',index=False)
Sau khi submit mô hình, mình đạt được kết quả là 4455.96312 trên private board, và 4419.17292 trên publish board. Đây là một kết quả khá tệ (đứng hạng khoảng top 300). Sau khi mình nhìn lại mô hình thì phát hiện một số vấn đề.
Các đặc trưng trong file features.csv nó không có mối tương quan gì hết với số bán như phân tích ở trên -> mình mạnh dạng bỏ luôn file features.csv, không quan tâm đến nó nữa, tập trung vào file chính.
Bỏ mấy cái lag luôn, thử forecast chính vào cái số bán luôn xem sao
Với cửa hàng nào thì xây dựng mô hình cho cửa hàng và sản phẩm đó, không xây dựng một mô hình tổng quát áp dụng cho toàn cửa hàng. với những cửa hàng không có trong tập train hoặc những sản phẩm mà cửa hàng đó chưa bán trước đây (nói chung là không có trong tập train) thì mới áp dụng mô hình của toàn cửa hàng cho nó.
Kết quả là mình đạt được 2736 trên private board và 2657.40087 trên publish board (top 30), kết quả trên vẫn làm cho mình chưa hài lòng lắm.
Cảm ơn các bạn đã theo dõi. Hẹn gặp bạn ở các bài viết tiếp theo.
Comments