2019年3月16日 星期六

[ML] 機器學習初學觀念-Regression


Regression
預測數值型問題可以使用的模型



  • Logistic Regression 使用Regression觀念解決分類問題, 預測值是給0~1的機率

  • 可使用Feature transform 來增加模型複雜度, 使其可以fit非線性問題, 再擴充feature必須考慮overfitting, 範例如下
In [12]:
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=3, include_bias=False)
X2 = poly.fit_transform(X)
print(X2)
[[   1.    1.    1.]
 [   2.    4.    8.]
 [   3.    9.   27.]
 [   4.   16.   64.]
 [   5.   25.  125.]]
The derived feature matrix has one column representing $x$, and a second column representing $x^2$, and a third column representing $x^3$. Computing a linear regression on this expanded input gives a much closer fit to our data:
In [13]:
model = LinearRegression().fit(X2, y)
yfit = model.predict(X2)
plt.scatter(x, y)
plt.plot(x, yfit);
  • from sklearn.base import BaseEstimator, TransformerMixin
    
    class GaussianFeatures(BaseEstimator, TransformerMixin):
        """Uniformly spaced Gaussian features for one-dimensional input"""
        
        def __init__(self, N, width_factor=2.0):
            self.N = N
            self.width_factor = width_factor
        
        @staticmethod
        def _gauss_basis(x, y, width, axis=None):
            arg = (x - y) / width
            return np.exp(-0.5 * np.sum(arg ** 2, axis))
            
        def fit(self, X, y=None):
            # create N centers spread along the data range
            self.centers_ = np.linspace(X.min(), X.max(), self.N)
            self.width_ = self.width_factor * (self.centers_[1] - self.centers_[0])
            return self
            
        def transform(self, X):
            return self._gauss_basis(X[:, :, np.newaxis], self.centers_,
                                     self.width_, axis=1)
        
    gauss_model = make_pipeline(GaussianFeatures(20),
                                LinearRegression())
    gauss_model.fit(x[:, np.newaxis], y)
    yfit = gauss_model.predict(xfit[:, np.newaxis])
    
    plt.scatter(x, y)
    plt.plot(xfit, yfit)
    plt.xlim(0, 10);
  • mse每筆誤差是垂直於x軸, 非垂直於model 線

  • 找最佳解有兩種方式, 
  1. 使用closed-form(每一個預測跟實際值差最小, scikit-learn使用此方法建議資料量在 < 100k ) 
  2. 使用gradient descent, 2比較適合大量資料
  • 將類別型的feature encode 成onehot比單一數字好, regression模型會認為有先後順序, 如果是真的有順序如s,m,l,xl...等就可以直接轉為數字
  • Regularization Regression : 為了避免overfitting, 希望找到最佳解但theta不要過大, 入 由我們給定他的參數設定範圍, 大部分feature會影響結果使用Ridge(算平方, theta不要大), 小部分使用Lasso(算絕對值, theta不要太正或太負, )會使許多參數變為0相當於降維, 速度快 , Elastic-net就自己決定比例, alpha值越大控制力越強
  • 如果你的模型中有很多變量對模型都有些許影響, 那麼用Ridge; 如果你的模型中只有少量變量對模型很大影響,那麼用LASSOLASSO可以使得很多變量的係數為0(相當於降維), 但是Ridge卻不能. 因為Ridge計算起來更快,所以當數據量特別大的時候, 更傾向於用Ridge. 最萬能的方法是用LASSO和Ri​​dge都試一試, 比較兩者Cross Validation的結果

  • Gradient descent
  • alpha值決定一次要走多遠(learning rate)
  • SGD每一步都只要看一筆資料走下一步, GD要把全部資料看完, 資料量大或有重複資料的時候不會使用GD, 並且有機會跨過local min
  • min-batch GD折衷, 一次看一群資料, 看完就update, 稱為一個epoch
  • Evaluation
  • MSE.RMSE(MSE開根號). MAE, 但數值沒有normalization, 不知道這數值代表多少只能與其他數值比較, 也會受到極端值影響
  • R2 score : 小於1, 越大越好 ,1- SSE(sum of square error)/SST(sum of square total) , SSE小R就會小 , 分數較容易讓人知道model是否優秀




Example: Predicting Bicycle Traffic

As an example, let's take a look at whether we can predict the number of bicycle trips across Seattle's Fremont Bridge based on weather, season, and other factors. We have seen this data already in Working With Time Series.
In this section, we will join the bike data with another dataset, and try to determine the extent to which weather and seasonal factors—temperature, precipitation, and daylight hours—affect the volume of bicycle traffic through this corridor. Fortunately, the NOAA makes available their daily weather station data (I used station ID USW00024233) and we can easily use Pandas to join the two data sources. We will perform a simple linear regression to relate weather and other information to bicycle counts, in order to estimate how a change in any one of these parameters affects the number of riders on a given day.
In particular, this is an example of how the tools of Scikit-Learn can be used in a statistical modeling framework, in which the parameters of the model are assumed to have interpretable meaning. As discussed previously, this is not a standard approach within machine learning, but such interpretation is possible for some models.
Let's start by loading the two datasets, indexing by date:
In [14]:
# !curl -o FremontBridge.csv https://data.seattle.gov/api/views/65db-xm6k/rows.csv?accessType=DOWNLOAD
In [15]:
import pandas as pd
counts = pd.read_csv('FremontBridge.csv', index_col='Date', parse_dates=True)
weather = pd.read_csv('data/BicycleWeather.csv', index_col='DATE', parse_dates=True)
Next we will compute the total daily bicycle traffic, and put this in its own dataframe:
In [16]:
daily = counts.resample('d').sum()
daily['Total'] = daily.sum(axis=1)
daily = daily[['Total']] # remove other columns
We saw previously that the patterns of use generally vary from day to day; let's account for this in our data by adding binary columns that indicate the day of the week:
In [17]:
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
for i in range(7):
    daily[days[i]] = (daily.index.dayofweek == i).astype(float)
Similarly, we might expect riders to behave differently on holidays; let's add an indicator of this as well:
In [18]:
from pandas.tseries.holiday import USFederalHolidayCalendar
cal = USFederalHolidayCalendar()
holidays = cal.holidays('2012', '2016')
daily = daily.join(pd.Series(1, index=holidays, name='holiday'))
daily['holiday'].fillna(0, inplace=True)
We also might suspect that the hours of daylight would affect how many people ride; let's use the standard astronomical calculation to add this information:
In [19]:
def hours_of_daylight(date, axis=23.44, latitude=47.61):
    """Compute the hours of daylight for the given date"""
    days = (date - pd.datetime(2000, 12, 21)).days
    m = (1. - np.tan(np.radians(latitude))
         * np.tan(np.radians(axis) * np.cos(days * 2 * np.pi / 365.25)))
    return 24. * np.degrees(np.arccos(1 - np.clip(m, 0, 2))) / 180.

daily['daylight_hrs'] = list(map(hours_of_daylight, daily.index))
daily[['daylight_hrs']].plot()
plt.ylim(8, 17)
Out[19]:
(8, 17)
We can also add the average temperature and total precipitation to the data. In addition to the inches of precipitation, let's add a flag that indicates whether a day is dry (has zero precipitation):
In [20]:
# temperatures are in 1/10 deg C; convert to C
weather['TMIN'] /= 10
weather['TMAX'] /= 10
weather['Temp (C)'] = 0.5 * (weather['TMIN'] + weather['TMAX'])

# precip is in 1/10 mm; convert to inches
weather['PRCP'] /= 254
weather['dry day'] = (weather['PRCP'] == 0).astype(int)

daily = daily.join(weather[['PRCP', 'Temp (C)', 'dry day']])
Finally, let's add a counter that increases from day 1, and measures how many years have passed. This will let us measure any observed annual increase or decrease in daily crossings:
In [21]:
daily['annual'] = (daily.index - daily.index[0]).days / 365.
Now our data is in order, and we can take a look at it:
In [22]:
daily.head()
Out[22]:
TotalMonTueWedThuFriSatSunholidaydaylight_hrsPRCPTemp (C)dry dayannual
Date
2012-10-033521.00.00.01.00.00.00.00.00.011.2773590.013.351.00.000000
2012-10-043475.00.00.00.01.00.00.00.00.011.2191420.013.601.00.002740
2012-10-053148.00.00.00.00.01.00.00.00.011.1610380.015.301.00.005479
2012-10-062006.00.00.00.00.00.01.00.00.011.1030560.015.851.00.008219
2012-10-072142.00.00.00.00.00.00.01.00.011.0452080.015.851.00.010959
With this in place, we can choose the columns to use, and fit a linear regression model to our data. We will set fit_intercept = False, because the daily flags essentially operate as their own day-specific intercepts:
In [23]:
# Drop any rows with null values
daily.dropna(axis=0, how='any', inplace=True)

column_names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'holiday',
                'daylight_hrs', 'PRCP', 'dry day', 'Temp (C)', 'annual']
X = daily[column_names]
y = daily['Total']

model = LinearRegression(fit_intercept=False)
model.fit(X, y)
daily['predicted'] = model.predict(X)
Finally, we can compare the total and predicted bicycle traffic visually:
In [24]:
daily[['Total', 'predicted']].plot(alpha=0.5);
It is evident that we have missed some key features, especially during the summer time. Either our features are not complete (i.e., people decide whether to ride to work based on more than just these) or there are some nonlinear relationships that we have failed to take into account (e.g., perhaps people ride less at both high and low temperatures). Nevertheless, our rough approximation is enough to give us some insights, and we can take a look at the coefficients of the linear model to estimate how much each feature contributes to the daily bicycle count:
In [25]:
params = pd.Series(model.coef_, index=X.columns)
params
Out[25]:
Mon              504.882756
Tue              610.233936
Wed              592.673642
Thu              482.358115
Fri              177.980345
Sat            -1103.301710
Sun            -1133.567246
holiday        -1187.401381
daylight_hrs     128.851511
PRCP            -664.834882
dry day          547.698592
Temp (C)          65.162791
annual            26.942713
dtype: float64
These numbers are difficult to interpret without some measure of their uncertainty. We can compute these uncertainties quickly using bootstrap resamplings of the data:
In [26]:
from sklearn.utils import resample
np.random.seed(1)
err = np.std([model.fit(*resample(X, y)).coef_
              for i in range(1000)], 0)
With these errors estimated, let's again look at the results:
In [27]:
print(pd.DataFrame({'effect': params.round(0),
                    'error': err.round(0)}))
              effect  error
Mon            505.0   86.0
Tue            610.0   83.0
Wed            593.0   83.0
Thu            482.0   85.0
Fri            178.0   81.0
Sat          -1103.0   80.0
Sun          -1134.0   83.0
holiday      -1187.0  163.0
daylight_hrs   129.0    9.0
PRCP          -665.0   62.0
dry day        548.0   33.0
Temp (C)        65.0    4.0
annual          27.0   18.0
We first see that there is a relatively stable trend in the weekly baseline: there are many more riders on weekdays than on weekends and holidays. We see that for each additional hour of daylight, 129 ± 9 more people choose to ride; a temperature increase of one degree Celsius encourages 65 ± 4 people to grab their bicycle; a dry day means an average of 548 ± 33 more riders, and each inch of precipitation means 665 ± 62 more people leave their bike at home. Once all these effects are accounted for, we see a modest increase of 27 ± 18 new daily riders each year.


Ref:
  • Jake VanderPlas著, 何敏煌譯 , Python資料科學學習手冊 , O'REILLY
  • 台灣人工智慧學校

沒有留言:

張貼留言