From 2f8fad26238d8baf5d6f8efbd1522378adea6b22 Mon Sep 17 00:00:00 2001 From: Priya Sundaram Date: Sun, 6 Sep 2026 16:46:22 +0000 Subject: [PATCH] Re-enable five disabled algorithms and the perceptron Re-enable the four scikit-learn machine-learning examples and the neural-network perceptron that had been disabled (renamed to .broken.txt / .DISABLED), and modernize them so they import and run cleanly on current scikit-learn and pass the doctest CI: machine_learning/gaussian_naive_bayes.py machine_learning/random_forest_classifier.py - Replace the removed sklearn.metrics.plot_confusion_matrix with ConfusionMatrixDisplay.from_estimator (removed in scikit-learn 1.2). - Drop the artificial time.sleep() calls. machine_learning/gradient_boosting_regressor.py machine_learning/random_forest_regressor.py - Replace the removed load_boston dataset (removed in scikit-learn 1.2 for ethical reasons) with the bundled load_diabetes dataset so the examples run offline. - Avoid an unused-variable lint (RUF059). neural_network/perceptron.py - Use a dedicated seeded random.Random instance instead of the global random state, so training is reproducible and thread-safe under the parallel test runner. - Cap training at epoch_number epochs so it always terminates even on non-linearly-separable data (previously an unbounded while True). - Have training() and sort() return their results instead of printing, per the contribution guidelines, and update the doctests accordingly. Requested by @cclauss in #8029; perceptron follow-up to #15206. --- ....py.broken.txt => gaussian_naive_bayes.py} | 15 +-- ...ken.txt => gradient_boosting_regressor.py} | 36 +++---- ...broken.txt => random_forest_classifier.py} | 7 +- ....broken.txt => random_forest_regressor.py} | 22 +++-- .../{perceptron.py.DISABLED => perceptron.py} | 96 ++++++++++--------- 5 files changed, 93 insertions(+), 83 deletions(-) rename machine_learning/{gaussian_naive_bayes.py.broken.txt => gaussian_naive_bayes.py} (76%) rename machine_learning/{gradient_boosting_regressor.py.broken.txt => gradient_boosting_regressor.py} (66%) rename machine_learning/{random_forest_classifier.py.broken.txt => random_forest_classifier.py} (91%) rename machine_learning/{random_forest_regressor.py.broken.txt => random_forest_regressor.py} (64%) rename neural_network/{perceptron.py.DISABLED => perceptron.py} (70%) diff --git a/machine_learning/gaussian_naive_bayes.py.broken.txt b/machine_learning/gaussian_naive_bayes.py similarity index 76% rename from machine_learning/gaussian_naive_bayes.py.broken.txt rename to machine_learning/gaussian_naive_bayes.py index 7e9a8d7f6dcf..6af4b210ba53 100644 --- a/machine_learning/gaussian_naive_bayes.py.broken.txt +++ b/machine_learning/gaussian_naive_bayes.py @@ -1,20 +1,17 @@ # Gaussian Naive Bayes Example -import time from matplotlib import pyplot as plt from sklearn.datasets import load_iris -from sklearn.metrics import accuracy_score, plot_confusion_matrix +from sklearn.metrics import ConfusionMatrixDisplay, accuracy_score from sklearn.model_selection import train_test_split from sklearn.naive_bayes import GaussianNB def main(): - """ Gaussian Naive Bayes Example using sklearn function. Iris type dataset is used to demonstrate algorithm. """ - # Load Iris dataset iris = load_iris() @@ -27,23 +24,21 @@ def main(): # Gaussian Naive Bayes nb_model = GaussianNB() - time.sleep(2.9) - model_fit = nb_model.fit(x_train, y_train) - y_pred = model_fit.predict(x_test) # Predictions on the test set + nb_model.fit(x_train, y_train) + y_pred = nb_model.predict(x_test) # Predictions on the test set # Display Confusion Matrix - plot_confusion_matrix( + ConfusionMatrixDisplay.from_estimator( nb_model, x_test, y_test, display_labels=iris["target_names"], - cmap="Blues", # although, Greys_r has a better contrast... + cmap="Blues", normalize="true", ) plt.title("Normalized Confusion Matrix - IRIS Dataset") plt.show() - time.sleep(1.8) final_accuracy = 100 * accuracy_score(y_true=y_test, y_pred=y_pred) print(f"The overall accuracy of the model is: {round(final_accuracy, 2)}%") diff --git a/machine_learning/gradient_boosting_regressor.py.broken.txt b/machine_learning/gradient_boosting_regressor.py similarity index 66% rename from machine_learning/gradient_boosting_regressor.py.broken.txt rename to machine_learning/gradient_boosting_regressor.py index c082f3cafe10..40deb11ebe6b 100644 --- a/machine_learning/gradient_boosting_regressor.py.broken.txt +++ b/machine_learning/gradient_boosting_regressor.py @@ -1,33 +1,37 @@ """Implementation of GradientBoostingRegressor in sklearn using the - boston dataset which is very popular for regression problem to - predict house price. +diabetes dataset, a popular regression problem used to predict +disease progression one year after baseline. + +Note: this example previously used the Boston house-price dataset, +which was removed from scikit-learn (>=1.2) for ethical reasons. +``load_diabetes`` is a drop-in bundled alternative that ships with +scikit-learn, so the example runs offline. """ import matplotlib.pyplot as plt import pandas as pd -from sklearn.datasets import load_boston +from sklearn.datasets import load_diabetes from sklearn.ensemble import GradientBoostingRegressor from sklearn.metrics import mean_squared_error, r2_score from sklearn.model_selection import train_test_split def main(): - - # loading the dataset from the sklearn - df = load_boston() + # loading the dataset from sklearn + df = load_diabetes() print(df.keys()) - # now let construct a data frame - df_boston = pd.DataFrame(df.data, columns=df.feature_names) - # let add the target to the dataframe - df_boston["Price"] = df.target + # now let's construct a data frame + df_data = pd.DataFrame(df.data, columns=df.feature_names) + # let's add the target to the dataframe + df_data["Target"] = df.target # print the first five rows using the head function - print(df_boston.head()) + print(df_data.head()) # Summary statistics - print(df_boston.describe().T) + print(df_data.describe().T) # Feature selection - x = df_boston.iloc[:, :-1] - y = df_boston.iloc[:, -1] # target variable + x = df_data.iloc[:, :-1] + y = df_data.iloc[:, -1] # target variable # split the data with 75% train and 25% test sets. x_train, x_test, y_train, y_test = train_test_split( x, y, random_state=0, test_size=0.25 @@ -43,7 +47,7 @@ def main(): test_score = model.score(x_test, y_test).round(3) print("Training score of GradientBoosting is :", training_score) print("The test score of GradientBoosting is :", test_score) - # Let us evaluation the model by finding the errors + # Let us evaluate the model by finding the errors y_pred = model.predict(x_test) # The mean squared error @@ -52,7 +56,7 @@ def main(): print(f"Test Variance score: {r2_score(y_test, y_pred):.2f}") # So let's run the model against the test data - fig, ax = plt.subplots() + _fig, ax = plt.subplots() ax.scatter(y_test, y_pred, edgecolors=(0, 0, 0)) ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "k--", lw=4) ax.set_xlabel("Actual") diff --git a/machine_learning/random_forest_classifier.py.broken.txt b/machine_learning/random_forest_classifier.py similarity index 91% rename from machine_learning/random_forest_classifier.py.broken.txt rename to machine_learning/random_forest_classifier.py index 3267fa209660..d77c37aadb87 100644 --- a/machine_learning/random_forest_classifier.py.broken.txt +++ b/machine_learning/random_forest_classifier.py @@ -1,18 +1,17 @@ # Random Forest Classifier Example + from matplotlib import pyplot as plt from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestClassifier -from sklearn.metrics import plot_confusion_matrix +from sklearn.metrics import ConfusionMatrixDisplay from sklearn.model_selection import train_test_split def main(): - """ Random Forest Classifier Example using sklearn function. Iris type dataset is used to demonstrate algorithm. """ - # Load Iris dataset iris = load_iris() @@ -28,7 +27,7 @@ def main(): rand_for.fit(x_train, y_train) # Display Confusion Matrix of Classifier - plot_confusion_matrix( + ConfusionMatrixDisplay.from_estimator( rand_for, x_test, y_test, diff --git a/machine_learning/random_forest_regressor.py.broken.txt b/machine_learning/random_forest_regressor.py similarity index 64% rename from machine_learning/random_forest_regressor.py.broken.txt rename to machine_learning/random_forest_regressor.py index 1001931a109d..1be8d6240594 100644 --- a/machine_learning/random_forest_regressor.py.broken.txt +++ b/machine_learning/random_forest_regressor.py @@ -1,24 +1,28 @@ # Random Forest Regressor Example -from sklearn.datasets import load_boston + +from sklearn.datasets import load_diabetes from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error from sklearn.model_selection import train_test_split def main(): - """ Random Forest Regressor Example using sklearn function. - Boston house price dataset is used to demonstrate the algorithm. - """ + The diabetes dataset is used to demonstrate the algorithm. - # Load Boston house price dataset - boston = load_boston() - print(boston.keys()) + Note: this example previously used the Boston house-price dataset, + which was removed from scikit-learn (>=1.2) for ethical reasons. + ``load_diabetes`` is a drop-in bundled alternative that ships with + scikit-learn, so the example runs offline. + """ + # Load the diabetes dataset + diabetes = load_diabetes() + print(diabetes.keys()) # Split dataset into train and test data - x = boston["data"] # features - y = boston["target"] + x = diabetes["data"] # features + y = diabetes["target"] x_train, x_test, y_train, y_test = train_test_split( x, y, test_size=0.3, random_state=1 ) diff --git a/neural_network/perceptron.py.DISABLED b/neural_network/perceptron.py similarity index 70% rename from neural_network/perceptron.py.DISABLED rename to neural_network/perceptron.py index 487842067ca3..241f27492edf 100644 --- a/neural_network/perceptron.py.DISABLED +++ b/neural_network/perceptron.py @@ -1,12 +1,15 @@ """ - Perceptron - w = w + N * (d(k) - y) * x(k) +Perceptron +w = w + N * (d(k) - y) * x(k) - Using perceptron network for oil analysis, with Measuring of 3 parameters - that represent chemical characteristics we can classify the oil, in p1 or p2 - p1 = -1 - p2 = 1 +Using perceptron network for oil analysis, with Measuring of 3 parameters +that represent chemical characteristics we can classify the oil, in p1 or p2 +p1 = -1 +p2 = 1 + +Reference: https://en.wikipedia.org/wiki/Perceptron """ + import random @@ -18,6 +21,7 @@ def __init__( learning_rate: float = 0.01, epoch_number: int = 1000, bias: float = -1, + seed: int | None = 0, ) -> None: """ Initializes a Perceptron network for oil analysis @@ -26,6 +30,8 @@ def __init__( :param learning_rate: learning rate used in optimizing. :param epoch_number: number of epochs to train network on. :param bias: bias value for the network. + :param seed: seed for the (internal) random number generator so that + training is reproducible; pass ``None`` for non-deterministic weights. >>> p = Perceptron([], (0, 1, 2)) Traceback (most recent call last): @@ -54,29 +60,36 @@ def __init__( self.number_sample = len(sample) self.col_sample = len(sample[0]) # number of columns in dataset self.weight: list = [] + # A dedicated RNG instance keeps training reproducible without touching + # the global ``random`` state (which other code/tests may rely on). + self._rng = random.Random(seed) - def training(self) -> None: + def training(self) -> int: """ - Trains perceptron for epochs <= given number of epochs - :return: None + Trains the perceptron until it stops misclassifying the training data + or the maximum number of epochs (``epoch_number``) is reached, whichever + comes first. The epoch cap guarantees termination even if the data is + not linearly separable. + + :return: the number of epochs the network was trained for. + >>> data = [[2.0149, 0.6192, 10.9263]] >>> targets = [-1] - >>> perceptron = Perceptron(data,targets) - >>> perceptron.training() # doctest: +ELLIPSIS - ('\\nEpoch:\\n', ...) - ... + >>> perceptron = Perceptron(data, targets) + >>> perceptron.training() + 5 """ for sample in self.sample: sample.insert(0, self.bias) for _ in range(self.col_sample): - self.weight.append(random.random()) + self.weight.append(self._rng.random()) self.weight.insert(0, self.bias) epoch_count = 0 - while True: + while epoch_count < self.epoch_number: has_misclassified = False for i in range(self.number_sample): u = 0 @@ -92,28 +105,28 @@ def training(self) -> None: * self.sample[i][j] ) has_misclassified = True - # print('Epoch: \n',epoch_count) epoch_count = epoch_count + 1 - # if you want control the epoch or just by error + # stop early once every sample is classified correctly if not has_misclassified: - print(("\nEpoch:\n", epoch_count)) - print("------------------------\n") - # if epoch_count > self.epoch_number or not error: break - def sort(self, sample: list[float]) -> None: + return epoch_count + + def sort(self, sample: list[float]) -> int: """ + Classifies a single observation as P1 (-1) or P2 (1). The network must + be trained first. + :param sample: example row to classify as P1 or P2 - :return: None + :return: -1 if the sample is classified as P1, otherwise 1 + >>> data = [[2.0149, 0.6192, 10.9263]] >>> targets = [-1] - >>> perceptron = Perceptron(data,targets) - >>> perceptron.training() # doctest: +ELLIPSIS - ('\\nEpoch:\\n', ...) - ... - >>> perceptron.sort([-0.6508, 0.1097, 4.0009]) # doctest: +ELLIPSIS - ('Sample: ', ...) - classification: P... + >>> perceptron = Perceptron(data, targets) + >>> perceptron.training() + 5 + >>> perceptron.sort([2.0149, 0.6192, 10.9263]) + -1 """ if len(self.sample) == 0: raise ValueError("Sample data can not be empty") @@ -122,23 +135,16 @@ def sort(self, sample: list[float]) -> None: for i in range(self.col_sample + 1): u = u + self.weight[i] * sample[i] - y = self.sign(u) - - if y == -1: - print(("Sample: ", sample)) - print("classification: P1") - else: - print(("Sample: ", sample)) - print("classification: P2") + return self.sign(u) def sign(self, u: float) -> int: """ threshold function for classification :param u: input number - :return: 1 if the input is greater than 0, otherwise -1 - >>> data = [[0],[-0.5],[0.5]] - >>> targets = [1,-1,1] - >>> perceptron = Perceptron(data,targets) + :return: 1 if the input is greater than or equal to 0, otherwise -1 + >>> data = [[0], [-0.5], [0.5]] + >>> targets = [1, -1, 1] + >>> perceptron = Perceptron(data, targets) >>> perceptron.sign(0) 1 >>> perceptron.sign(-0.5) @@ -224,8 +230,8 @@ def sign(self, u: float) -> int: network = Perceptron( sample=samples, target=target, learning_rate=0.01, epoch_number=1000, bias=-1 ) - network.training() - print("Finished training perceptron") + epochs = network.training() + print(f"Finished training perceptron in {epochs} epoch(s)") print("Enter values to predict or q to exit") while True: sample: list = [] @@ -235,4 +241,6 @@ def sign(self, u: float) -> int: break observation = float(user_input) sample.insert(i, observation) - network.sort(sample) + classification = network.sort(sample) + label = "P1" if classification == -1 else "P2" + print(f"Sample: {sample} classification: {label}")