1+ """
2+ AdaBoost implementation for binary classification using decision stumps.
3+
4+ Reference: https://en.wikipedia.org/wiki/AdaBoost
5+
6+ >>> import numpy as np
7+ >>> X = np.array([[0, 0], [1, 1], [1, 0], [0, 1]])
8+ >>> y = np.array([0, 1, 1, 0])
9+ >>> clf = AdaBoost(n_estimators=5)
10+ >>> clf.fit(X, y)
11+ >>> clf.predict(np.array([[0, 0], [1, 1]]))
12+ array([0, 1])
13+ """
114
215import numpy as np
16+ from typing import Any , Dict , List
17+
318
4- # AdaBoost implementation for binary classification
5- # Uses decision stumps (one-level trees) as weak learners
619class AdaBoost :
7- def __init__ (self , n_estimators = 50 ):
8- # Number of boosting rounds
9- self .n_estimators = n_estimators
10- self .alphas = [] # Weights for each weak learner
11- self .models = [] # List of weak learners (stumps)
20+ def __init__ (self , n_estimators : int = 50 ) -> None :
21+ """Initialize AdaBoost classifier.
22+ Args:
23+ n_estimators: Number of boosting rounds.
24+ """
25+ self .n_estimators : int = n_estimators
26+ self .alphas : List [float ] = [] # Weights for each weak learner
27+ self .models : List [Dict [str , Any ]] = [] # List of weak learners (stumps)
1228
13- def fit (self , X , y ):
14- # X: (n_samples, n_features), y: (n_samples,) with labels 0 or 1
29+ def fit (self , X : np .ndarray , y : np .ndarray ) -> None :
30+ """Fit AdaBoost model.
31+ Args:
32+ X: (n_samples, n_features) feature matrix
33+ y: (n_samples,) labels (0 or 1)
34+ """
1535 n_samples , n_features = X .shape
1636 w = np .ones (n_samples ) / n_samples # Initialize sample weights
1737 self .models = []
@@ -20,8 +40,8 @@ def fit(self, X, y):
2040 for _ in range (self .n_estimators ):
2141 # Train a decision stump with weighted samples
2242 stump = self ._build_stump (X , y_ , w )
23- pred = stump [' pred' ]
24- err = stump [' error' ]
43+ pred = stump [" pred" ]
44+ err = stump [" error" ]
2545 # Compute alpha (learner weight)
2646 alpha = 0.5 * np .log ((1 - err ) / (err + 1e-10 ))
2747 # Update sample weights
@@ -30,20 +50,35 @@ def fit(self, X, y):
3050 self .models .append (stump )
3151 self .alphas .append (alpha )
3252
33- def predict (self , X ):
34- # Aggregate predictions from all weak learners
53+ def predict (self , X : np .ndarray ) -> np .ndarray :
54+ """Predict class labels for samples in X.
55+ Args:
56+ X: (n_samples, n_features) feature matrix
57+ Returns:
58+ (n_samples,) predicted labels (0 or 1)
59+ >>> import numpy as np
60+ >>> X = np.array([[0, 0], [1, 1], [1, 0], [0, 1]])
61+ >>> y = np.array([0, 1, 1, 0])
62+ >>> clf = AdaBoost(n_estimators=5)
63+ >>> clf.fit(X, y)
64+ >>> clf.predict(np.array([[0, 0], [1, 1]]))
65+ array([0, 1])
66+ """
3567 clf_preds = np .zeros (X .shape [0 ])
3668 for alpha , stump in zip (self .alphas , self .models ):
37- pred = self ._stump_predict (X , stump ['feature' ], stump ['threshold' ], stump ['polarity' ])
69+ pred = self ._stump_predict (
70+ X , stump ["feature" ], stump ["threshold" ], stump ["polarity" ]
71+ )
3872 clf_preds += alpha * pred
39- # Return final prediction (majority vote)
4073 return np .where (clf_preds >= 0 , 1 , 0 )
4174
42- def _build_stump (self , X , y , w ):
43- # Find the best decision stump for current weights
75+ def _build_stump (
76+ self , X : np .ndarray , y : np .ndarray , w : np .ndarray
77+ ) -> Dict [str , Any ]:
78+ """Find the best decision stump for current weights."""
4479 n_samples , n_features = X .shape
45- min_error = float (' inf' )
46- best_stump = {}
80+ min_error = float (" inf" )
81+ best_stump : Dict [ str , Any ] = {}
4782 for feature in range (n_features ):
4883 thresholds = np .unique (X [:, feature ])
4984 for threshold in thresholds :
@@ -53,16 +88,18 @@ def _build_stump(self, X, y, w):
5388 if error < min_error :
5489 min_error = error
5590 best_stump = {
56- ' feature' : feature ,
57- ' threshold' : threshold ,
58- ' polarity' : polarity ,
59- ' error' : error ,
60- ' pred' : pred .copy ()
91+ " feature" : feature ,
92+ " threshold" : threshold ,
93+ " polarity" : polarity ,
94+ " error" : error ,
95+ " pred" : pred .copy (),
6196 }
6297 return best_stump
6398
64- def _stump_predict (self , X , feature , threshold , polarity ):
65- # Predict using a single decision stump
99+ def _stump_predict (
100+ self , X : np .ndarray , feature : int , threshold : float , polarity : int
101+ ) -> np .ndarray :
102+ """Predict using a single decision stump."""
66103 pred = np .ones (X .shape [0 ])
67104 if polarity == 1 :
68105 pred [X [:, feature ] < threshold ] = - 1
0 commit comments