Prophet là một project open source được xây dựng bởi Facebook Data Science Team giúp forecasting timeseries, Prophet dựa trên mô hình dự báo ARIMA, được bổ sung thêm non-linear trend phù hợp với tính thời vụ theo ngày, tuần, tháng, năm cộng với tác động từ holiday, giúp cho việc đưa ra dự báo chính xác hơn và đảm bảo việc hiểu dữ liệu càng nhiều giúp mô hình dự báo càng chính xác.

Trang chủ Prophet: Prophet homepage link.

Tài liệu này là một bản dịch của document tại https://facebook.github.io/prophet/docs

Điểm mạnh của Prophet:

  • Chính xác và nhanh chóng: tốt hơn hầù hết các phương pháp khác trong nhiều trường hợp, Khớp Stan model nên hoạt động nhanh.
  • Hoàn toàn tự động: nhận một dự báo có ý nghĩa trên một data lộ xộn mà không cần những nổ lực thủ công.
  • Có thể điều chỉnh dự báo: cho phép điều chỉnh để cải thiện khả năng dự báo dựa trên miền kiến thức dự báo.
  • Sử dụng ngôn ngữ R, Python.
  • Dễ tiếp cận, chúng ta không cần biết quá hiều về code, bạn cũng không cần phải biết về AI. Thân thiệt dễ dùng, không quá khó hiểu với tất cả mọi người.

Điểm yếu dễ thấy nhất là do đây là một mô hình tuyến tính, dù bổ sung thêm trend không tuyến tính thì cũng không thể làm gì với những chuỗi thời gian mà không có quy luật rõ ràng, ví dụ như dự đoán giá Bitcoin, giá cổ phiếu, những thứ dễ bị tác động làm thay đổi trend bởi các yếu tố không xác định trước.

Nên để bắt đầu bạn cần hiểu timeseries của bạn có quy luật gì nổi trội hay không, có bị ảnh hưởng bởi những sự kiện có thể dự báo trước không, nếu câu trả lời là có thì Prophet nhất định sẽ có ích cho bạn. Nếu không, có thể bạn phải qua một số bước xử lý để data của bạn trông có quy luật hơn, hoặc sử dụng một mô hình khác cho forecasting.

Tl;dr: Forecasting at scale

1. Bắt đầu với Prophet

Data mẫu cho thực hành các bạn tải tại đây: test_data.csv

Prophet tuân theo mô hình API của sklearn. Đầu vào của Prophet luôn là một dataframe với 2 cột: ds và y. ds có nghĩa là datestamp tuân theo chuẩn ngày tháng của Pandas YYYY-MM-DD hoặc YYYY-MM-DD HH:MM:SS, cột y thể hiện giá trị biểu thị phép đo mà ta muốn dự đoán. Ta có thể nhận input từ file csv bằng hàm read_csv của pandas. Ví dụ:

df = pd.read_csv('./example.csv')

Để dự đoán ta tạo ra instance của Prophet class. Mọi setting cho dự đoán ta cung cấp sẽ được đưa vào constructor. Ta fit model bằng cách đưa dữ liệu quá khứ vào hàm fit dưới dạng dataframe. quá trình dự đoán sẽ tốn 1-5s:

instance = Prophet()
instance.fit(df)

Prophet sẽ tạo ra một dataframe với 1 cột là ds chứa ngày tháng, khi tiến hành dự đoán thì nó sẽ thiêm một cột yhat chứa giá trị dự đoán tương ứng với ngày tháng ở ds vào bên cạnh. Tuy nhiên ở cột ds mà Prophet tạo ra chỉ chứa những ngày trong lịch sử mà ta cung cấp, Nếu muốn dự đoán kết quả trong những ngày tới ta phải thêm những ngày đó vào cột ds, điều này được thự hiện bằng method make_future_dataframe. Cụ thể nếu muốn tạo dự đoán 7 ngày tiếp theo kể từ ngày cuối cùng trong file csv mà ta truyền lúc đầu, ta gọi:

future = instance.make_future_dataframe(periods=7)

Bây giờ ta truyền future này vào trong prophet instance để nó tạo ra dataframe chứa dự đoán trong quá khứ và những ngày tiếp theo:

future = instance.make_future_dataframe(periods=7)

Bây giờ ta truyền future này vào trong prophet instance để nó tạo ra dataframe chứa dự đoán trong quá khứ và những ngày tiếp theo:

forecast = instance.predict(future)

Trong dataframe forecast này chứa các cột ds,yhat,yhat_lower, yhat_upper với ý nghĩa như sau:

NameÝ nghĩa
dsngày tháng năm
yhatgiá trị dự đoán
yhat_lowergiá trị dự đoán thấp nhất
yhat_uppergiá trị dự đoán cao nhất

Ta plot dataframe forecast này để xem kết quả:

fig1 = m.plot(forecast)
fig2 = m.plot_components(forecast)

2. Saturating Forecasts

Mặc định là Prophet sẽ dùng linear trend model để dự đoán. Ta thường sử dụng linear nêú như giá trị của ta có thể tăng mà không biết được tối đa là bao nhiêu. Lấy ví dụ nếu nói về dân số thế giới, thì quy mô dân số thế giới sẽ tăng linear, ta không biết được số dân đông nhất sẽ đạt là bao nhiêu. Một ví dụ đối lập, xét số lượng account mà người dùng tạo, giả sử ràng buộc một người được dùng một account duy nhất thì số lượng account sẽ có điểm bão hòa là khi nó đạt tới bằng với quy mô dân số, khi đó số account sẽ không tăng nữa. Vậy để điều chỉnh Prophet cho phù hợp với nhu cầu dự đoán ta phải hiểu được giá trị dự đoán của ta có bão hòa khi đạt một mức nào không. Và khi nó có đạt bão hòa ta sử dụng logistic growth trend model. Hiểu và chọn một mô hình phù hợp sẽ cho một kết quả dự đoán tốt hơn.
Để sử dụng logistic trend model ta phải biết được mức tối da và mức tối thiểu của giá trị y trong dataframe sẽ đạt (không phải là max và min của y). Ta cần thêm 2 trường này vào trong dataframe bên cạnh 2 trường mặc định ds và y:

df['cap'] = 8.5
df['floor'] = 1
instance = Prophet(growth='logistic')
instance.fit(df)

Lưu ý ta cần thêm cap và floor vào future dataframe cho đồng bộ với historical dataframe.

future['cap']=8.5
future['floor']=1

Sau đó cho predict như bình thường.

3 Trend Changepoints

Trong timeseries, sẽ có những thay đổi đột ngột trong chuỗi, Prophet mặc định sẽ tự xác định những điểm changepoint này và cập nhật vào trend một cách thích hợp. Nếu chúng ta muốn kiểm soát việc này thì Prophet cho chúng ta làm điều đó. Tuy nhiên theo đề nghị, chúng ta không nên kiểm soát số changepoint trừ khi prophet miss các changepoint quan trọng hoặc bị overfit khi quá khớp.
Có các thông số ta có thể thay đổi là:

  • n_changepoints: giới hạn số changepoint, số này lớn hơn 1, và không có tối đa, tùy vào độ dài của timeseries của ta mà xác định hoặc tốt nhất là để Prophet tự xác định.
  • changepoint_range: mặc định changepoint được xác định bởi 80% phía trước trên timeseries để tránh overfitting. Con số này mặc định là 0.8. Nhưng ta có thể điều chỉnh nó lên 1 để xác định changepoint trên toàn bộ dữ liệu lịch sử hoặc set xuống còn 0.5 để dùng phân nửa dữ liệu lịch sử cho dự đoán.
  • changepoint_prior_scale: Giá trị lớn hơn 0, không có giới hạn, mặc định là 0.05. Nếu Trend của ta bị overfit hoặc underfit ta có thể điều chỉnh strength của sparse prior bằng cách điều chỉnh biến này, tăng nó thì trend sẽ càng flexible, giảm nó thì trend sẽ ít flexible hơn và changepoint sẽ khớp với data hơn, ta không thể biết tăng hay giảm sẽ phù hợp, nên còn tùy trường hợp. xem hình sau đây:
    Khi điều chỉnh số prior scale =0.5 thì ta có vùng màu xanh nhạt trong dự đoán sẽ rộng:

Khi điều chỉnh số này bằng 0.01 thì vùng xanh nhạt thu hẹp lại như sau:

Ngoài ra ta có thể tự xác định changepoint bằng cách đưa ngày tháng vào biến changepoints khi khởi tạo prophet instance:

instance = Prophet(changepoints=['2014-01-01'])

Chúng ta không nên tự xác định mà hãy để prophet làm điều này.

Vậy ở đây chúng ta chỉ nên quan tâm tới changepoint_range và changepoint_prior_scale. Còn lại tốt nhất là nên để mặc định.

4 Seasonality, Holiday Effects và Regressor

Đây là phần quan trọng có ảnh hưởng khá nhiều đến mô hình. Đầu tiền ta xét đến holiday và special events. Sau đó sẽ xét tới các chỉ số ảnh hưởng đến seasonality, thêm một custom seasonality.

4.1 Modeling Holidays và Special Event

Để prophet nhận holidays và special events ta phải đưa chúng vào một dataframe, dataframe của ta phải có 2 columns là holiday và ds. trong đó holiday là tên của event, còn dsdatestamp , chúng ta cũng có thêm 2 cột phụ là lower_window và upper_window để mở rộng thời gian của event ta muốn, trong đó lower_window diễn tả số ngày trở về trước và upper_window diễn tả số ngày về sau. lưu ý lower_window < 0. Ngoài ra ta còn có thêm một cột là prior_scale nếu muốn điều chỉnh mức tác động của từng ngày lễ lên dự đoán Xem ví dụ sau:

playoffs = pd.DataFrame({
  'holiday': 'playoff',
  'ds': pd.to_datetime(['2008-01-13', '2009-01-03', '2010-01-16',
                        '2010-01-24', '2010-02-07', '2011-01-08',
                        '2013-01-12', '2014-01-12', '2014-01-19',
                        '2014-02-02', '2015-01-11', '2016-01-17',
                        '2016-01-24', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
  'prior_scale' : 0.05,
})
superbowls = pd.DataFrame({
  'holiday': 'superbowl',
  'ds': pd.to_datetime(['2010-02-07', '2014-02-02', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
  'prior_scale' : 0.1,
})
holidays = pd.concat((playoffs, superbowls))
instance = Prophet(holidays=holidays)

Ta có thể xem effect của holliday trong dataframe forecast:

forecast[(forecast['playoff'] + forecast['superbowl']).abs() > 0][
        ['ds', 'playoff', 'superbowl']][-10:]

Khi ta gọi plot_components(forecast) thì effect của holliday cũng được vẽ ra.

4.2 Builtin Country Holidays

Prophet có tính sẵn một số ngày lễ cho một số quốc gia, để bật các ngày này lên ta sử dụng instance.add_country_holidays(country_name='VN'), ta có thể xem lại những ngày được thêm bằng cách gọi instance.train_holiday_names. Các ngày lễ lớn của các quốc gia đã được tính đến năm 2044.

4.3 Fourier Order for Seasonalities

Để hiểu thêm về chuỗi Fourier tham khảo tại đây
Fourier order xác định seasonality có thể thay đổi nhanh như thế nào. Khi tăng chỉ số này số vòng được thêm vào nhiều hơn dẫn đến các dao động tăng, khi giảm chỉ số này biểu đồ sẽ mịn hơn. Hình bên dưới đây minh họa về fourier series:

Nếu xác định một seasonality được thêm vào có những thay đổi tầng số cao và không mượt ta có thể tăng số này lên cao hơn thay vì 10 mặc định, ta cũng có thể giảm số này xuống nếu biết seasonality của ta mượt, ít dao động tần số cao.
ví dụ với fourier orther = 10 ta có được như sau:

Với fourier order = 20 ta được một biểu đồ có dao động cao hơn:

Vậy để có thể có một con số phù hợp ta phải hiểu rõ seasonality mà ta thêm vào để điều chỉnh cho phù hợp, tăng fourier lên thì sẽ khớp hơn với dữ liệu thực tế nhưng có thể dẫn đến overfit. Mỗi hành động điều có cái giá của nó, quan trọng là biết được như thế nào là tốt, đánh đổi phù hợp trong khoảng có thể chấp nhận được. Trong trường hợp chu kì có hình hài rõ ràng, có thể nhìn thấy được qua quan sát biểu đồ, ta có thể tăng chỉ số này lên, trong trường hợp trong một chu kì không có hình rõ ràng, lúc này lúc khác, để chỉ số này thấp là tốt nhất.

4.4 Specifying Custom Seasonalities

Nếu như các build-in seasonality như daily, weekly, yearly mà Prophet cung cấp là chưa đủ thì ta có thể thêm những custom seasonality của mình vào trong model cho phù hợp với hoàn cảnh. Việc thêm custom seasonality sẽ giúp ta có thể kiểm soát đươc các seasonality của mình, điều chỉnh các thông số theo ý muốn thay vì sử dụng mặc định của Prophet. Đặc biệt là những sự kiện xảy ra theo chu kì không phải là theo tuần, theo tháng, theo năm mà diễn ra khoảng 10 ngày 1 một lần (khuyến mãi 20% của nhà mạng Viettel) hoặc theo chu kì 2 tháng, 1 quý. Kiếm soát tần suất thay đổi của từng seasonality bằng fourier order cũng rất có ý nghĩa. có những thay đổi theo tuần dao động mạnh theo từng ngày cần fourier oder lớn, cũng có những thay đổi nhẹ vài tháng một lần ta cần chỉnh fourier order thấp để có được dự đoán tốt hơn. Ngoài ra ta còn có thể kiểm soát prior scale cho phù hợn với seasonality.
Để thêm một custom seasonality ta đơn giản gọi hàm add_seasonality như sau:

instance.add_seasonality(name='quaterly', period=91.5, fourier_order=5, prior_scale = 0.05)

4.5 Seasonalities dựa trên factors riêng

Trong một số trường hợp thì seasonality có thể dựa trên một số factor ví dụ biểu đồ bán áo tắm trong tuần mùa xuân sẽ khác với mùa hè, mùa xuân bán đồ tắm ngày thứ 7, chủ nhật cao nhiều lần so với ngày thường, còn mùa hè bán điều mỗi ngày, etc. Vì thế Prophet cho ta bật một seasonality dựa theo thời gian và tắt nó trong một khoảng thời gian thông qua conditional seasonality.

Để thêm điều kiện cho seasonality, đầu tiên ta định nghĩa một hàm để hỗ trợ. Ở ví dụ dưới đây ta bật seasonality từ đầu tháng 9 đến cuối tháng 1 năm sau:

#define func that help us return true if a month is from Sep to Jan
def is_nfl_season(ds):
    date = pd.to_datetime(ds)
    return (date.month > 8 or date.month < 2)

#set true to row which month satisfy condition we've defined in is_nfl_season() 
df['on_season'] = df['ds'].apply(is_nfl_season)
df['off_season'] = ~df['ds'].apply(is_nfl_season)

#create prophet instance and set weekly seasonality to False
instance = Prophet(weekly_seasonality=False)
#add custom seasonality with condition name
instance.add_seasonality(name='weekly_on_season', period=7, fourier_order=3, condition_name='on_season')
instance.add_seasonality(name='weekly_off_season', period=7, fourier_order=3, condition_name='off_season')
#we need to set true to row that satisfy conditon on future dataframe becasue Prophet dont do it automatically
future['on_season'] = future['ds'].apply(is_nfl_season)
future['off_season'] = ~future['ds'].apply(is_nfl_season)

Khi plot components ta sẽ có được thêm 2 chart sau:

4.6 Prior scale cho holidays và seasonality

Ngoài cách chỉnh bằng cách thêm prior scale vào holiday dataframe thì ta có thể đưa thông số này vào lúc tạo Prophet instance bằng cách gọi instance = Prophet(holidays=holidays, holidays_prior_scale=0.05). Còn đối với custom seasonaly ta chi cần cấp giá trị cho biến prior_scale lúc thêm seasonality add_seasonality(name='weekly', period=7, fourier_order=3, prior_scale=0.1)

4.7 Additional regressors

Để thêm các hiệu ứng bổ sung (additional effect) vào một số ngày đặc biệt ta có thể dùng additional regressor.
Additional regressors có thể được thêm vào phần linear của model bằng cách sử dụng hàm add_regressor. Ta cần thêm cột là tên của regressor vào trong historical dataframe và future dataframe. Xem cách làm sau:

def nfl_sunday(ds):
    date = pd.to_datetime(ds)
    if date.weekday() == 6 and (date.month > 8 or date.month < 2):
        return 1
    else:
        return 0
df['nfl_sunday'] = df['ds'].apply(nfl_sunday)

instance = Prophet()
instancem.add_regressor('nfl_sunday')
instancem.fit(df)

future['nfl_sunday'] = future['ds'].apply(nfl_sunday)

forecast = m.predict(future)
fig = m.plot_components(forecast)

Khi plot ra sẽ có thêm một phần:

5. Multiplicative Seasonality

Theo mặc định Prophet sẽ giả sử seasonality là cố định và không bị ảnh hưởng bởi trend, nhưng trong một số trường hợp thì seasonality phát triển theo trend, tức là trend lên cao thì biên độ dao động theo chiều dọc sẽ mở rộng và ngược lại.

Với mode bằng additive:

với mode bằng multiplicative

Vì điều này, để có thể dự đoán tốt hơn thì ta phải nói cho Prophet biết seasonality có thay đổi theo trend hay không, bằng cách set giá trị biến seasonality_mode='multiplicative' trong khởi tạo Prophet instance. Khi set giá trị này mọi seasonality của ta điều sẽ thay đổi dựa theo trend. Tuy nhiên ta có thể điều chỉnh với từng custom seasonality/additional regressor bằng cách chỉnh biến mode lúc thêm seasonality/regressor vào Prophet. xem ví dụ sau:





instance = Prophet(seasonality_mode='multiplicative')
instance.add_seasonality('quarterly', period=91.25, fourier_order=8, mode='additive')
instance.add_regressor('regressor', mode='additive')

Một điểm cần lưu ý nữa là khi mode = additive thì prior scale không nên đặt cao vì biểu đồ sẽ không có thay đổi quá lớn khi trend thay đổi.

6. Uncertainty Intervals

Theo mặc định thì Prophet sẽ trả về các khoảng không chắc chắn cho dự báo yhat (khoảng màu xanh nhạt). Có một số giả định quan trọng đằng sau những uncertainty interval này.
Có ba nguồn của sự không chắc chắn trong dự báo là sự không chắc chắn ở trend, seasonality estimates và additional observation noise. Nguồn lớn nhất là tiền năng thay đổi trend trong tương lai, Prophet giả định tuần suất và cường độ trung bình của trend change trong tương lại sẽ giống như những thay đổi của trend trong quá khứ. Prophet dự báo trend change trong tương lai và tính phân phối của chúng để ra incertainty intervals. Như đã nói ở trend changepoint phần prior scale, tăng prior scale sẽ mở rộng vùng không chắc chắn trong dự đoán tương lai. Độ rộng của cùng không chắc chắn được đặt mặc định là o.8 tương đương 80% tuy nhiên ta có thể thay đổi bằng cách đặt giá trị cho biến interval_width trong lúc khởi tạo Prophet instance.
Ta sẽ thường thấy uncertainty interval chỉ ra tương lai sẽ có cùng tần số và tốc độ thay đổi như quá khứ, nhưng điều này có thể không đúng và giá trị thực không được đảm bảo rằng sẽ nằm trong khoảng từ biên trên đến biên dưới của khoảng không chắc chắc chắn. Nó thể vượt qua vùng này.

mặc định thì Prophet chỉ trả về phần phông chắc chắn trong trend và trong observation noise. Đối với seasonality ta phải lấy mẫu bayesian đầy đủ. Việc này có thể thực hiện đơn giản bằng cách đặt giá trị cho biến mcmc_sample trong lúc khởi tạo prophet instance, giá trị mặc định của biến này là 0 tức là không lấy mẫu ngày nào, biến này phải lớn hơn 1 và là số nguyên, giá trị của nó ứng với số ngày cần lấy mẫu bayesian đầy đủ kể từ ngày đầu tiên trong dữ liệu historical. Khi plot components sẽ ra giống như sau:

7. Outliers

Xem biểu đồ bên dưới đây:

ta thấy trong khoảng thời gian 2010 tới 2011 có một khoảng outlier, những ngoại lệ này gây ảnh hưởng tới uncertain interval ở cuối khiến phần không chắc chắn mở ra rất rộng. Ảnh hưởng đến kết quả dự đoán. Ta có thể xóa đi phần outlier này để tránh làm ảnh hưởng đến mô hình dự đoán của mình. Khi xóa những value này thì Prophet sẽ không gặp vấn đề gì, ta set value bằng none nhưng vẫn giữ cột ds, Prophet khi không thấy giá trị sẽ để vào những giá trị mà nó dự đoán.

Ví dụ ta remove như sau:

df.loc[(df['ds'] > '2010-01-01') & (df['ds'] < '2011-01-01'), 'y'] = None
model = Prophet().fit(df)
fig = model.plot(model.predict(future))

Ta được như sau:

8 Non-daily data

Ngoài dự đoán theo ngày Prophet cũng có thể dự đoán với dữ liệu timeseries sub-daily, month. Để Prophet nhận dữ liệu sub-daily, mẫu timestamp phải có dạng YYYY-MM-DD HH:MM:SS, ngoài ra ta phải cung cấp giá trị cho biến freq thành freq='H' khi tạo future data frame bằng hàm make_future_dataframe. Khi ta cung cấp dữ liệu sub-daily thì daily_seasonality sẽ được bật.

Đối với dữ liệu subdaily bị miss một số giờ trong ngày, ta có thể tiến hành dự đoán cho những giờ mình có thay vì dự đoán luôn cho những giờ bị thiếu. ví dụ ta muốn dự đoán từ 0 giờ tới 6 giờ sáng ta có thể bỏ những hàng từ 6 đến 24 giờ trong future dataframe trước khi đưa nào vào Prophet instance để predict. xem ví dụ sau:

future2 = future.copy()
future2 = future2[future2['ds'].dt.hour < 6]
fcst = m.predict(future2)
fig = m.plot(fcst)

Đối với dữ liệu là tháng chứ không phải ngày, để prophet dự đoán chính xác cần đưa biến freq=’M’ vào hàm make_future_dataframe lúc khởi tạo future data frame.

9. Diagnostics

Prophet chứa timeseries cross validation để đo lường forecasting error sử dụng historical data. Prophet sẽ cắt một đoạn trên dữ liệu lịch sử và kiểm tra. Hàm cross_validation có các đối sau:

NameMeaning
initialsố ngày dùng để train bắt đầu từ ngày đầu tiên
horizonkhoảng cách từ ngày ở cuối series tới vùng dùng để test
periodkhoảng cách giữa initial và ngày đầu dùng để test

Theo mặc định thì vùng initial sẽ gấp 3 lần vùng horizon, cutoff bằng phần nửa horizon.

Cross validation sẽ ouput ra một dataframe với cột y là giá trị thực, yhat là giá trị dự đoán cho nơi dùng để train và cho cả vùng cutoff. Dataframe này có thể dùng để tính lỗi của yhat với y. Ví dụ:

df_cv = cross_validation(m, initial='730 days', period='180 days', horizon = '365 days')
df_cv.head()

performance_metrics có thể được sử dụng để tính toán hiệu suất dự đoán.

from fbprophet.diagnostics import performance_metrics
df_p = performance_metrics(df_cv)
df_p.head()

ta có thể thống kê hiệu suất thôn qua RMSE,MAE,MAPE bằng cách truyền tên hàm đánh giá vào

fig = plot_cross_validation_metric(df_cv, metric='mape')


Kích thước của rolling window có thể đc điều chỉnh thông qua biến rolling_window, giá trị mặc định của biến này là 0.1 ứng với 10% row của df_cv trong mỗi window. Tăng nó sẽ làm average curve mịn hơn.

Initial phải đủ dài để model có thể bắt hết những thành phần của seasonality và intitial

10 Kết

Vậy là ta đã đi qua hết những điều cơ bản về các sử dụng Prophet, cho việc dự đoán timeseries. Hi vọng mọi người có thể ứng dụng nó vào trong các bài toán thực tế phục vụ cho nhu cầu công việc, học tập. Bài dịch này được viết lúc mình mới bắt đầu tìm hiểu Prophet nên có những câu dịch như một cái máy chứ không thật sự hiểu nó nói gì. Sau một thời gian làm việc thì mình đã hiểu ý nghĩa nhưng vẫn chưa có thời gian sửa hết lại câu chữ, có thể phần dịch vẫn kiến cho mọi người bối rối, mọi người thấy có gì không hiểu có thể hỏi và để mình chỉnh lại chỗ bạn chưa hiểu cho mọi người cùng rõ chỗ đó nhé.

Lộc Lee

Leave a comment

Your email address will not be published. Required fields are marked *