MUR015 從物件導向思考線性迴歸分析

目錄

從物件導向思考線性迴歸分析

紫式晦澀每日一篇文章第15天

前言

  1. 今天是2022年第14天, 全年第2週, 一月的第二個週四. 昨日生成自己的「元工作流」以後, 感覺安排任務好很多, 可以更有精力去規劃任務!

  2. 今天的素材主要來自Datacamp課程-Object-Oriented Programming in Python 結合之前的研究經驗. 目標是用物件導向編程的角度來重新思考線性回歸方法.

物件導向基礎: 克隆的藍圖

編程範式: 從單純系統到複雜系統

  1. 過程式編程: 我們那個年代一開始程式教育的編程模式.
  • 過程式程序設計(英語:Procedural programming),又稱過程化編程,一種編程典範,衍生自指令式編程,有時會被視為是同義語。
  • 主要要採取過程調用或函數調用的方式來進行流程控制。
  1. 過程式編程的好處: 比較符合我們傳統的數學教育, 但對現代的Python編程就產生gap了.
  • Code as a sequence of steps 代碼=一串動作
  • Great for data analysis 適合資料分析

  1. 物件導向編程: 在物件導向程式編程裡,電腦程式會被設計成彼此相關的物件。 面向對象程序設計可以看作一種在程序中包含各種獨立而又互相調用的對象的思想.

  2. 物件導向編程的好處: 我們這個時代的編程思維.

  • Code as interactions of objects 代碼=物件之間的互動
  • Great for building frameworks and tools 建立框架與工具
  • Maintainable and reusable code! 好維護, 好重複使用

單純系統: 過程式編程 複雜系統: 物件導向編程

物件 as 資料結構; 類 as 藍圖

  1. 原則: 封裝 (Encapsulation): bundling data with code operating on it. 將「資料」與「作用他的代碼」捆綁在一起.

  2. 物件(Object): 定義物件, 思考其會有的狀態, 以及他需要的行為.

物件 = 狀態 + 行為 Object = State + Behavior

  1. 類(Class): 物件可能的「狀態」與「行為」的藍圖. Blueprint for objects outlining possible states and behaviors.

物件導向Python實踐: 狀態, 屬性, 變數; 行為, 方法, 函數.

  1. Python中的物件: 找到任何看到的Python code背後的藍圖.
  • Python中什麼都是「物件」. Everything in Python is an object.
  • 所有物件背後都有「類」. Every object has a class.
  • 用內建函數type()可以找到「物件背後的類」. Use type() to find the class

物件=狀態+行為; 狀態 as 物件屬性; 行為 as 物件方法

  1. 召喚Python物件資訊: 利用obj.可以讀取「屬性(attributes)」與「方法 (methods)」.

物件=狀態+行為; 數學物件=變數+函數; 編程物件=屬性+方法

  1. 類比物件導向編程與數學: 屬性對應變數; 方法對應函數
  • attribute 對應 variables 對應 obj.my_attribute
  • method 對應 function() 對應 obj.my_method()
  • 用內建函數dir()查詢所有的屬性與方法.

練習使用type()dir()

以物件導向理解線性迴歸模組: type(), dir()與help()

  1. 實踐物件導向思維於LinearRegression模組:
1# Program 01: Run linear regression.
2import numpy as np
3from sklearn.linear_model import LinearRegression

用type(LinearRegression)找到線性模型的藍圖(狀態與行為的封裝)

  1. 執行type(LinearRegression): 會跑出很簡潔的abc.ABCMeta
1type(LinearRegression)
2abc.ABCMeta

用dir(LinearRegression)找到線性模型的「狀態」與「行為」

  1. 執行dir(LinearRegression): 會跑出一大串類方法(class methods)! 其中有私有方法(Private method)也有公用方法(Public method).
 1dir(LinearRegression)
 2['__abstractmethods__',
 3 '__class__',
 4 '__delattr__',
 5 '__dict__',
 6 '__dir__',
 7 '__doc__',
 8 '__eq__',
 9 '__format__',
10 '__ge__',
11 '__getattribute__',
12 '__getstate__',
13 '__gt__',
14 '__hash__',
15 '__init__',
16 '__init_subclass__',
17 '__le__',
18 '__lt__',
19 '__module__',
20 '__ne__',
21 '__new__',
22 '__reduce__',
23 '__reduce_ex__',
24 '__repr__',
25 '__setattr__',
26 '__setstate__',
27 '__sizeof__',
28 '__str__',
29 '__subclasshook__',
30 '__weakref__',
31 '_abc_impl',
32 '_check_feature_names',
33 '_check_n_features',
34 '_decision_function',
35 '_estimator_type',
36 '_get_param_names',
37 '_get_tags',
38 '_more_tags',
39 '_preprocess_data',
40 '_repr_html_',
41 '_repr_html_inner',
42 '_repr_mimebundle_',
43 '_set_intercept',
44 '_validate_data',
45 'fit',
46 'get_params',
47 'predict',
48 'score',
49 'set_params']

用help(LinearRegression)找到「公用」的屬性與方法

公用方法(Public Method): 注意沒加底線的那些方法, 就是我們讀官方指南會先出現的, 給「用戶」所使用的方法

1 'fit',
2 'get_params',
3 'predict',
4 'score',
5 'set_params'

這些與官方API解釋的方法一致:

  1. 執行help(LinearRegression): 會出現詳細的文檔, 對公用的屬性與方法做詳細解釋
  1Help on class LinearRegression in module sklearn.linear_model._base:
  2
  3class LinearRegression(sklearn.base.MultiOutputMixin, sklearn.base.RegressorMixin, LinearModel)
  4 |  LinearRegression(*, fit_intercept=True, normalize='deprecated', copy_X=True, n_jobs=None, positive=False)
  5 |  
  6 |  Ordinary least squares Linear Regression.
  7 |  
  8 |  LinearRegression fits a linear model with coefficients w = (w1, ..., wp)
  9 |  to minimize the residual sum of squares between the observed targets in
 10 |  the dataset, and the targets predicted by the linear approximation.
 11 |  
 12 |  Parameters
 13 |  ----------
 14 |  fit_intercept : bool, default=True
 15 |      Whether to calculate the intercept for this model. If set
 16 |      to False, no intercept will be used in calculations
 17 |      (i.e. data is expected to be centered).
 18 |  
 19 |  normalize : bool, default=False
 20 |      This parameter is ignored when ``fit_intercept`` is set to False.
 21 |      If True, the regressors X will be normalized before regression by
 22 |      subtracting the mean and dividing by the l2-norm.
 23 |      If you wish to standardize, please use
 24 |      :class:`~sklearn.preprocessing.StandardScaler` before calling ``fit``
 25 |      on an estimator with ``normalize=False``.
 26 |  
 27 |      .. deprecated:: 1.0
 28 |         `normalize` was deprecated in version 1.0 and will be
 29 |         removed in 1.2.
 30 |  
 31 |  copy_X : bool, default=True
 32 |      If True, X will be copied; else, it may be overwritten.
 33 |  
 34 |  n_jobs : int, default=None
 35 |      The number of jobs to use for the computation. This will only provide
 36 |      speedup in case of sufficiently large problems, that is if firstly
 37 |      `n_targets > 1` and secondly `X` is sparse or if `positive` is set
 38 |      to `True`. ``None`` means 1 unless in a
 39 |      :obj:`joblib.parallel_backend` context. ``-1`` means using all
 40 |      processors. See :term:`Glossary <n_jobs>` for more details.
 41 |  
 42 |  positive : bool, default=False
 43 |      When set to ``True``, forces the coefficients to be positive. This
 44 |      option is only supported for dense arrays.
 45 |  
 46 |      .. versionadded:: 0.24
 47 |  
 48 |  Attributes
 49 |  ----------
 50 |  coef_ : array of shape (n_features, ) or (n_targets, n_features)
 51 |      Estimated coefficients for the linear regression problem.
 52 |      If multiple targets are passed during the fit (y 2D), this
 53 |      is a 2D array of shape (n_targets, n_features), while if only
 54 |      one target is passed, this is a 1D array of length n_features.
 55 |  
 56 |  rank_ : int
 57 |      Rank of matrix `X`. Only available when `X` is dense.
 58 |  
 59 |  singular_ : array of shape (min(X, y),)
 60 |      Singular values of `X`. Only available when `X` is dense.
 61 |  
 62 |  intercept_ : float or array of shape (n_targets,)
 63 |      Independent term in the linear model. Set to 0.0 if
 64 |      `fit_intercept = False`.
 65 |  
 66 |  n_features_in_ : int
 67 |      Number of features seen during :term:`fit`.
 68 |  
 69 |      .. versionadded:: 0.24
 70 |  
 71 |  feature_names_in_ : ndarray of shape (`n_features_in_`,)
 72 |      Names of features seen during :term:`fit`. Defined only when `X`
 73 |      has feature names that are all strings.
 74 |  
 75 |      .. versionadded:: 1.0
 76 |  
 77 |  See Also
 78 |  --------
 79 |  Ridge : Ridge regression addresses some of the
 80 |      problems of Ordinary Least Squares by imposing a penalty on the
 81 |      size of the coefficients with l2 regularization.
 82 |  Lasso : The Lasso is a linear model that estimates
 83 |      sparse coefficients with l1 regularization.
 84 |  ElasticNet : Elastic-Net is a linear regression
 85 |      model trained with both l1 and l2 -norm regularization of the
 86 |      coefficients.
 87 |  
 88 |  Notes
 89 |  -----
 90 |  From the implementation point of view, this is just plain Ordinary
 91 |  Least Squares (scipy.linalg.lstsq) or Non Negative Least Squares
 92 |  (scipy.optimize.nnls) wrapped as a predictor object.
 93 |  
 94 |  Examples
 95 |  --------
 96 |  >>> import numpy as np
 97 |  >>> from sklearn.linear_model import LinearRegression
 98 |  >>> X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
 99 |  >>> # y = 1 * x_0 + 2 * x_1 + 3
100 |  >>> y = np.dot(X, np.array([1, 2])) + 3
101 |  >>> reg = LinearRegression().fit(X, y)
102 |  >>> reg.score(X, y)
103 |  1.0
104 |  >>> reg.coef_
105 |  array([1., 2.])
106 |  >>> reg.intercept_
107 |  3.0...
108 |  >>> reg.predict(np.array([[3, 5]]))
109 |  array([16.])
110 |  
111 |  Method resolution order:
112 |      LinearRegression
113 |      sklearn.base.MultiOutputMixin
114 |      sklearn.base.RegressorMixin
115 |      LinearModel
116 |      sklearn.base.BaseEstimator
117 |      builtins.object
118 |  
119 |  Methods defined here:
120 |  
121 |  __init__(self, *, fit_intercept=True, normalize='deprecated', copy_X=True, n_jobs=None, positive=False)
122 |      Initialize self.  See help(type(self)) for accurate signature.
123 |  
124 |  fit(self, X, y, sample_weight=None)
125 |      Fit linear model.
126 |      
127 |      Parameters
128 |      ----------
129 |      X : {array-like, sparse matrix} of shape (n_samples, n_features)
130 |          Training data.
131 |      
132 |      y : array-like of shape (n_samples,) or (n_samples, n_targets)
133 |          Target values. Will be cast to X's dtype if necessary.
134 |      
135 |      sample_weight : array-like of shape (n_samples,), default=None
136 |          Individual weights for each sample.
137 |      
138 |          .. versionadded:: 0.17
139 |             parameter *sample_weight* support to LinearRegression.
140 |      
141 |      Returns
142 |      -------
143 |      self : object
144 |          Fitted Estimator.
145 |  
146 |  ----------------------------------------------------------------------
147 |  Data and other attributes defined here:
148 |  
149 |  __abstractmethods__ = frozenset()
150 |  
151 |  ----------------------------------------------------------------------
152 |  Data descriptors inherited from sklearn.base.MultiOutputMixin:
153 |  
154 |  __dict__
155 |      dictionary for instance variables (if defined)
156 |  
157 |  __weakref__
158 |      list of weak references to the object (if defined)
159 |  
160 |  ----------------------------------------------------------------------
161 |  Methods inherited from sklearn.base.RegressorMixin:
162 |  
163 |  score(self, X, y, sample_weight=None)
164 |      Return the coefficient of determination of the prediction.
165 |      
166 |      The coefficient of determination :math:`R^2` is defined as
167 |      :math:`(1 - \frac{u}{v})`, where :math:`u` is the residual
168 |      sum of squares ``((y_true - y_pred)** 2).sum()`` and :math:`v`
169 |      is the total sum of squares ``((y_true - y_true.mean()) ** 2).sum()``.
170 |      The best possible score is 1.0 and it can be negative (because the
171 |      model can be arbitrarily worse). A constant model that always predicts
172 |      the expected value of `y`, disregarding the input features, would get
173 |      a :math:`R^2` score of 0.0.
174 |      
175 |      Parameters
176 |      ----------
177 |      X : array-like of shape (n_samples, n_features)
178 |          Test samples. For some estimators this may be a precomputed
179 |          kernel matrix or a list of generic objects instead with shape
180 |          ``(n_samples, n_samples_fitted)``, where ``n_samples_fitted``
181 |          is the number of samples used in the fitting for the estimator.
182 |      
183 |      y : array-like of shape (n_samples,) or (n_samples, n_outputs)
184 |          True values for `X`.
185 |      
186 |      sample_weight : array-like of shape (n_samples,), default=None
187 |          Sample weights.
188 |      
189 |      Returns
190 |      -------
191 |      score : float
192 |          :math:`R^2` of ``self.predict(X)`` wrt. `y`.
193 |      
194 |      Notes
195 |      -----
196 |      The :math:`R^2` score used when calling ``score`` on a regressor uses
197 |      ``multioutput='uniform_average'`` from version 0.23 to keep consistent
198 |      with default value of :func:`~sklearn.metrics.r2_score`.
199 |      This influences the ``score`` method of all the multioutput
200 |      regressors (except for
201 |      :class:`~sklearn.multioutput.MultiOutputRegressor`).
202 |  
203 |  ----------------------------------------------------------------------
204 |  Methods inherited from LinearModel:
205 |  
206 |  predict(self, X)
207 |      Predict using the linear model.
208 |      
209 |      Parameters
210 |      ----------
211 |      X : array-like or sparse matrix, shape (n_samples, n_features)
212 |          Samples.
213 |      
214 |      Returns
215 |      -------
216 |      C : array, shape (n_samples,)
217 |          Returns predicted values.
218 |  
219 |  ----------------------------------------------------------------------
220 |  Methods inherited from sklearn.base.BaseEstimator:
221 |  
222 |  __getstate__(self)
223 |  
224 |  __repr__(self, N_CHAR_MAX=700)
225 |      Return repr(self).
226 |  
227 |  __setstate__(self, state)
228 |  
229 |  get_params(self, deep=True)
230 |      Get parameters for this estimator.
231 |      
232 |      Parameters
233 |      ----------
234 |      deep : bool, default=True
235 |          If True, will return the parameters for this estimator and
236 |          contained subobjects that are estimators.
237 |      
238 |      Returns
239 |      -------
240 |      params : dict
241 |          Parameter names mapped to their values.
242 |  
243 |  set_params(self, **params)
244 |      Set the parameters of this estimator.
245 |      
246 |      The method works on simple estimators as well as on nested objects
247 |      (such as :class:`~sklearn.pipeline.Pipeline`). The latter have
248 |      parameters of the form ``<component>__<parameter>`` so that it's
249 |      possible to update each component of a nested object.
250 |      
251 |      Parameters
252 |      ----------
253 |      **params : dict
254 |          Estimator parameters.
255 |      
256 |      Returns
257 |      -------
258 |      self : estimator instance
259 |          Estimator instance.

後記

  1. 延伸閱讀: 在MUR009 思辨註釋與文檔 我們有討論過註釋與文檔的差異. 現在看起來, 可以把「文檔(documentation)」理解為「面向用戶, 對公用方法的解釋」; 而「註釋(comment)理解為「面向開發者, 對私有方法的技術註解」.

  2. 到此整合了Datacamp課程-Object-Oriented Programming in Python 的邏輯, 結合之前研究經驗, 用物件導向的世界觀來理解線性回歸模組.

  3. 本文章分了三個面向. 從抽象逐漸具體, 涵蓋心法, 技法, 用法三個層次.

  • 第一個面向為「物件導向基礎: 克隆的藍圖」: 扮演心法的角色
  • 第二個面向為「物件導向Python實踐: 狀態, 屬性, 變數; 行為, 方法, 函數.」: 扮演技法的角色
  • 第三個面向為「以物件導向理解線性迴歸模組: type(), dir()help(): 扮演用法的角色.
  1. 感覺寫文章真的是思考最好的方式, 每天不斷的輸出, 讓各種文章寫作過程shape我們的思維. 不斷迭代往前, 逐漸專業, 用自己的宇宙觸碰物理宇宙或元宇宙. 天天向上, 共勉之!

2022.01.13. 紫蕊 於 西拉法葉, 印第安納, 美國.

版權

CC BY-NC-ND 4.0

評論