From 1860b7897e23b0a60f35b58107afe29e726e72cf Mon Sep 17 00:00:00 2001 From: "aleksandra.valerianova" Date: Fri, 13 Mar 2026 14:49:47 +0200 Subject: [PATCH 1/7] placeholder_text corrected --- .../Reading an image/task-info.yaml | 6 +++--- Comic-Con and K-means/Reading an image/task.py | 5 ++--- .../Gradient Descent/task-info.yaml | 2 +- .../Read data/task-info.yaml | 14 +++++++------- .../Read data/task.py | 14 +++++--------- .../Stochastic Gradient Descent/task-info.yaml | 4 ++-- 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Comic-Con and K-means/Reading an image/task-info.yaml b/Comic-Con and K-means/Reading an image/task-info.yaml index c04b55c..46cbf97 100644 --- a/Comic-Con and K-means/Reading an image/task-info.yaml +++ b/Comic-Con and K-means/Reading an image/task-info.yaml @@ -3,12 +3,12 @@ files: - name: task.py visible: true placeholders: - - offset: 156 + - offset: 153 length: 16 placeholder_text: "# TODO" - - offset: 243 + - offset: 231 length: 30 - placeholder_text: "# Reshape the image" + placeholder_text: "# TODO" - name: tests/test_task.py visible: false propagatable: false diff --git a/Comic-Con and K-means/Reading an image/task.py b/Comic-Con and K-means/Reading an image/task.py index b78f885..c37412f 100644 --- a/Comic-Con and K-means/Reading an image/task.py +++ b/Comic-Con and K-means/Reading an image/task.py @@ -3,10 +3,9 @@ def read_image(path='superman-batman.png'): - # Here, we load the image using PIL's open function. + # Here, load the image using PIL's open function. image = Image.open(path) - # We reshape the image into an (M x N, 3) - # array. + # Reshape the image into an (M x N, 3) array. return np.array(image).reshape(-1, 3) diff --git a/Pima Indians Diabetes and Linear Classifier/Gradient Descent/task-info.yaml b/Pima Indians Diabetes and Linear Classifier/Gradient Descent/task-info.yaml index bc46119..aade20a 100644 --- a/Pima Indians Diabetes and Linear Classifier/Gradient Descent/task-info.yaml +++ b/Pima Indians Diabetes and Linear Classifier/Gradient Descent/task-info.yaml @@ -21,7 +21,7 @@ files: placeholder_text: "# TODO: Set it to the new ones" - offset: 2210 length: 28 - placeholder_text: "# Return the predicted classes" + placeholder_text: "# TODO" - name: loss_functions.py visible: true - name: task.py diff --git a/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml b/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml index d0d71a1..a5dd1c6 100644 --- a/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml +++ b/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml @@ -3,18 +3,18 @@ files: - name: task.py visible: true placeholders: - - offset: 314 + - offset: 256 length: 35 placeholder_text: "# TODO" - - offset: 802 + - offset: 530 length: 36 - placeholder_text: "# Standardize the dataset" - - offset: 1000 + placeholder_text: "# TODO" + - offset: 709 length: 60 - placeholder_text: "# Add a column of -1 to the left of X" - - offset: 1145 + placeholder_text: "# TODO" + - offset: 822 length: 12 - placeholder_text: "# {0, 1} -> {1, -1}" + placeholder_text: "# TODO" - name: tests/test_task.py visible: false propagatable: false diff --git a/Pima Indians Diabetes and Linear Classifier/Read data/task.py b/Pima Indians Diabetes and Linear Classifier/Read data/task.py index 78df4cc..986e496 100644 --- a/Pima Indians Diabetes and Linear Classifier/Read data/task.py +++ b/Pima Indians Diabetes and Linear Classifier/Read data/task.py @@ -6,23 +6,19 @@ # and returns it as a pair of arrays: features # and diabetes presence. def read_data(fname): - # The genfromtxt method loads data from a text file and splits columns - # based on the provided delimiter. + # Load data from a CSV file using numpy.genfromtxt. data = np.genfromtxt(fname, delimiter=',') # The data is split into X (all columns but the last) and # y (the last column). X, y = data[:, :-1], data[:, -1] - # The features are rescaled: - # X is standardized by centering features around the mean - # with a unit standard deviation. This means that the mean - # and standard deviation of the standard scores are 0 and 1, respectively. - # This procedure is recommended for data that follows a normal distribution. + # Normalize features: for each column subtract its mean + # and divide by its standard deviation. X = (X - X.mean(axis=0)) / X.std(axis=0) - # A column of -1s is prepended to the left of the X array. + # Add a column of -1s to the left of X. # It acts as a pseudo-feature that simplifies our vector # calculations later on. X = np.concatenate((-np.ones(len(X)).reshape(-1, 1), X), axis=1) - # y is standardized: centered around 0 with a standard deviation of 1. + # Convert labels from {0,1} to {1,-1}. y = -(y * 2 - 1) return X, y diff --git a/Pima Indians Diabetes and Linear Classifier/Stochastic Gradient Descent/task-info.yaml b/Pima Indians Diabetes and Linear Classifier/Stochastic Gradient Descent/task-info.yaml index f31373a..f847ae2 100644 --- a/Pima Indians Diabetes and Linear Classifier/Stochastic Gradient Descent/task-info.yaml +++ b/Pima Indians Diabetes and Linear Classifier/Stochastic Gradient Descent/task-info.yaml @@ -5,14 +5,14 @@ files: placeholders: - offset: 1114 length: 32 - placeholder_text: "# TODO: Generate the batch" + placeholder_text: "# TODO" - offset: 1172 length: 60 placeholder_text: "# TODO: Calculate the gradient using the current weights, X\ \ and y batches" - offset: 2183 length: 25 - placeholder_text: "# TODO: Initialize it here" + placeholder_text: "# TODO" - name: gradient_descent.py visible: true - name: loss_functions.py From 34f4e6a1f49c74321020f3da1c04d8963e84e57b Mon Sep 17 00:00:00 2001 From: "aleksandra.valerianova" Date: Fri, 13 Mar 2026 16:43:13 +0200 Subject: [PATCH 2/7] Hints for Horror Trees and Bayes Guards are done --- Bayes Guards SMS/Predict/task.md | 102 ++++++++++++++++++++++++++ Horror Trees/Information Gain/task.md | 23 +++++- Horror Trees/Node/node.py | 2 + Horror Trees/Node/task-info.yaml | 4 +- Horror Trees/Predict/task.md | 78 ++++++++++++++++++++ 5 files changed, 203 insertions(+), 6 deletions(-) diff --git a/Bayes Guards SMS/Predict/task.md b/Bayes Guards SMS/Predict/task.md index c660f3d..c9f74c8 100644 --- a/Bayes Guards SMS/Predict/task.md +++ b/Bayes Guards SMS/Predict/task.md @@ -59,6 +59,108 @@ one is the largest among them and choose a class corresponding to it from you may use the numpy.argmax function. +
+For every message, you need to convert its words into dictionary indices. +Create an array that will store the index of each unique word. + +```python +index_array = np.zeros(unique.shape, dtype=np.int64) +``` + +
+ +
+ +Each word must be mapped to its index in `self.dictionary.` + +If the word exists in the dictionary, use its stored index. +If it does not exist, use the special index for unknown words: `self.dict_size` (length of the dictionary). + +```python +for i, word in enumerate(unique): + word_index = self.dictionary[word] if word in self.dictionary else self.dict_size +``` +**Why do this?** + +Because `self.likelihood` stores word probabilities by index, not by the word itself. + +
+ +
+ +`self.likelihood` stores the probabilities of words for each class. + +Its shape is: +`number_of_classes * (dictionary_size + 1)` + + +- each **row** corresponds to a class +- each **column** corresponds to a word index in the dictionary + +For example, `self.likelihood[c, w]` represents the probability of word `w` +appearing in class `c`. + +To compute the likelihood of the current message, select only the columns +that correspond to the words in the message and apply the logarithm: + +```python +log_likelihood = np.log(self.likelihood[:, index_array]) +``` +
+ +
+ +At this point, `log_likelihood` already contains the log-probabilities of the words from the current message for **each class**. + +Now you need to get one final score for every class. + +To do this you need to combine two things: +
    +
  • how typical the words of the message are for that class
  • +
  • how common that class is in the training data
  • +
+ +The first part is obtained from log_likelihood. +The second part is the logarithm of the prior probability of the class stored in classes_prior. + +Therefore, add the log prior probability to the summed log-likelihood values: + +```python +posterior = np.log(self.classes_prior) + np.sum(log_likelihood, axis=1) +``` +
+ +
+At this point, posterior contains one score for each class. +These scores represent how likely the message belongs to each class. + +Your task is to select the class with the highest score. + +First, find the index of the largest value in the posterior array. +You can use numpy.argmax for this — it returns the position of the maximum element. + +Then use this index to retrieve the corresponding class label from self.unique_classes. + +
predicted = self.unique_classes[np.argmax(posterior)]
+ +This selects the class with the highest posterior score. +
+ +
+The classifier already knows how to predict class labels using the predict method. +However, we also need a simple way to check how well the model performs. + +The score method does this by comparing the predicted labels with the true labels and computing the fraction of matches. + +You can obtain the predicted labels by calling predict. + +When two arrays are compared with ==, NumPy produces a boolean array where each position shows whether the prediction is correct. +Summing this array gives the number of correct predictions. +To obtain the required value, divide this number by the total number of objects. + +
return np.sum(self.predict(X) == y) / len(y)
+
+ To see the results of your code, you can add the following lines to the `main` block in `task.py` and then run it: diff --git a/Horror Trees/Information Gain/task.md b/Horror Trees/Information Gain/task.md index 40063ab..aa86946 100644 --- a/Horror Trees/Information Gain/task.md +++ b/Horror Trees/Information Gain/task.md @@ -23,9 +23,6 @@ takes a sample, divides it into two independent sub-samples, and calculates the To divide the sample, use the `divide` method written in the previous step. - - -
To calculate information gain, you can use the above formula in the following way: @@ -46,4 +43,22 @@ To see the results of your code, you can add the following line to the print(f'Information Gain: {predicate.information_gain(X, y)}\n') ``` The variables required for this code to function were introduced in previous steps. If you haven't worked with -`task.py` yet, ensure they are properly defined. \ No newline at end of file +`task.py` yet, ensure they are properly defined. + +
+ +Just use `divide` method of the `Predicate` class to divide the sample into two subsets. + +`X1, y1, X2, y2 = self.divide(X, y)` + +
+ +
+After the split, each subset contributes to the final entropy proportionally to its size. Therefore we compute the fraction of +samples that went to the first subset. + +Use `float(...)` to ensure that the division produces a floating-point number (a probability between 0 and 1) rather than an integer. + +`p = float(len(X1)) / len(X)` + +
diff --git a/Horror Trees/Node/node.py b/Horror Trees/Node/node.py index 0db1120..05056c9 100644 --- a/Horror Trees/Node/node.py +++ b/Horror Trees/Node/node.py @@ -4,6 +4,8 @@ # the true and false branches. class Node: def __init__(self, column=-1, value=None, true_branch=None, false_branch=None): + # Implement the four attributes of the Node class: + # column, value, true_branch, false_branch self.column = column self.value = value self.true_branch = true_branch diff --git a/Horror Trees/Node/task-info.yaml b/Horror Trees/Node/task-info.yaml index d84a40b..c3c975a 100644 --- a/Horror Trees/Node/task-info.yaml +++ b/Horror Trees/Node/task-info.yaml @@ -3,9 +3,9 @@ files: - name: node.py visible: true placeholders: - - offset: 333 + - offset: 443 length: 127 - placeholder_text: "# TODO: Implement the four attributes of the Node class" + placeholder_text: "# TODO" - name: task.py visible: true - name: tests/test_task.py diff --git a/Horror Trees/Predict/task.md b/Horror Trees/Predict/task.md index bdb85d0..18e015f 100644 --- a/Horror Trees/Predict/task.md +++ b/Horror Trees/Predict/task.md @@ -25,3 +25,81 @@ print(f'Class prediction for object X[0]: {tree.predict(X[0])}\n') ``` The variables required for this code to function were introduced in previous steps. If you haven't worked with `task.py` yet, ensure they are properly defined. + +
+ +A decision tree predicts a class by **moving from the root to a leaf**. +If the current element is already a class label (not a `Node`), the traversal is finished. + +```python +if not isinstance(sub_tree, Node): + return sub_tree +``` + +
+ +
+ +Each node checks one feature of the object being classified. +The node stores which feature to check in `sub_tree.column`. +So you need to take that value from `x`. + +```python +v = x[sub_tree.column] +``` + +
+ +
+ +Some features are numeric (for example, age or income). +In this case the node represents a **threshold condition**, such as: + +`age >= 30` + +So if the feature value is numeric, compare it with the node’s threshold to decide which branch to follow. + +```python +if isinstance(v, int) or isinstance(v, float): + if v >= sub_tree.value: + branch = sub_tree.true_branch + else: + branch = sub_tree.false_branch +``` + +
+ +
+ +Other features may be categorical (for example, color or country). +In this case the node checks **equality** with the stored value. + +For example: `color == "red"` + +Choose the branch depending on whether the value matches the condition. + +```python +else: + if v == sub_tree.value: + branch = sub_tree.true_branch + else: + branch = sub_tree.false_branch +``` + +
+ +
+ +After choosing the next branch, the classification is **not finished yet**. +You have only moved **one step down the tree**. + +The selected branch may still be another node that asks the next question. +So you need to **repeat the same process** for the new subtree. + +The easiest way to repeat the same logic is to **call the same function again** with the new branch. + +```python +return self.classify_subtree(x, branch) +``` +The recursion continues until a leaf node (class label) is reached, which is then returned. +
\ No newline at end of file From 4936a517b360532dd7ed79bdbfcd9f44fa8c4aa3 Mon Sep 17 00:00:00 2001 From: "aleksandra.valerianova" Date: Tue, 17 Mar 2026 16:35:13 +0200 Subject: [PATCH 3/7] Applied text changes --- .../Read data/task-info.yaml | 6 +++--- .../Read data/task.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml b/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml index a5dd1c6..ca00b97 100644 --- a/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml +++ b/Pima Indians Diabetes and Linear Classifier/Read data/task-info.yaml @@ -6,13 +6,13 @@ files: - offset: 256 length: 35 placeholder_text: "# TODO" - - offset: 530 + - offset: 532 length: 36 placeholder_text: "# TODO" - - offset: 709 + - offset: 703 length: 60 placeholder_text: "# TODO" - - offset: 822 + - offset: 812 length: 12 placeholder_text: "# TODO" - name: tests/test_task.py diff --git a/Pima Indians Diabetes and Linear Classifier/Read data/task.py b/Pima Indians Diabetes and Linear Classifier/Read data/task.py index 986e496..7cd1146 100644 --- a/Pima Indians Diabetes and Linear Classifier/Read data/task.py +++ b/Pima Indians Diabetes and Linear Classifier/Read data/task.py @@ -11,14 +11,14 @@ def read_data(fname): # The data is split into X (all columns but the last) and # y (the last column). X, y = data[:, :-1], data[:, -1] - # Normalize features: for each column subtract its mean - # and divide by its standard deviation. + # Standardize features: subtract the mean + # and divide by the standard deviation for each column. X = (X - X.mean(axis=0)) / X.std(axis=0) - # Add a column of -1s to the left of X. + # Prepend a column of -1s to X. # It acts as a pseudo-feature that simplifies our vector # calculations later on. X = np.concatenate((-np.ones(len(X)).reshape(-1, 1), X), axis=1) - # Convert labels from {0,1} to {1,-1}. + # Map labels from {0,1} to {1,-1}. y = -(y * 2 - 1) return X, y From 2eeb09efd4907a25ebcb2304939be5b154029ade Mon Sep 17 00:00:00 2001 From: "aleksandra.valerianova" Date: Tue, 17 Mar 2026 17:47:02 +0200 Subject: [PATCH 4/7] Changed hints in Horror Trees --- Horror Trees/Information Gain/task.md | 40 ++++++++-------- Horror Trees/Predict/task.md | 66 ++++++++++----------------- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/Horror Trees/Information Gain/task.md b/Horror Trees/Information Gain/task.md index aa86946..aeb3e30 100644 --- a/Horror Trees/Information Gain/task.md +++ b/Horror Trees/Information Gain/task.md @@ -15,15 +15,29 @@ We subtract entropy `Y` for the condition `X` from entropy `Y` to calculate the `Y`, provided that there is some additional knowledge `X` about `Y`. - ### Task Implement the `information_gain` method, which takes a sample, divides it into two independent sub-samples, and calculates the information gain. To divide the sample, use the `divide` method written -in the previous step. +in the previous step. + +
+ +Just use `divide` method of the `Predicate` class to divide the sample into two subsets. + +`X1, y1, X2, y2 = self.divide(X, y)` + +
+ +
+Each subset contributes to entropy proportionally to its size, so we compute the fraction of samples in the first subset: + +`p = float(len(X1)) / len(X)` + +
-
+
To calculate information gain, you can use the above formula in the following way: @@ -43,22 +57,4 @@ To see the results of your code, you can add the following line to the print(f'Information Gain: {predicate.information_gain(X, y)}\n') ``` The variables required for this code to function were introduced in previous steps. If you haven't worked with -`task.py` yet, ensure they are properly defined. - -
- -Just use `divide` method of the `Predicate` class to divide the sample into two subsets. - -`X1, y1, X2, y2 = self.divide(X, y)` - -
- -
-After the split, each subset contributes to the final entropy proportionally to its size. Therefore we compute the fraction of -samples that went to the first subset. - -Use `float(...)` to ensure that the division produces a floating-point number (a probability between 0 and 1) rather than an integer. - -`p = float(len(X1)) / len(X)` - -
+`task.py` yet, ensure they are properly defined. \ No newline at end of file diff --git a/Horror Trees/Predict/task.md b/Horror Trees/Predict/task.md index 18e015f..3f3cd34 100644 --- a/Horror Trees/Predict/task.md +++ b/Horror Trees/Predict/task.md @@ -13,36 +13,23 @@ In `classify_subtree`, you need to: 1. Check whether `sub_tree` is an instance of the `Node` class and if yes, return the current value of `sub_tree`, as in such a case, it is a class label. -2. Compare the characteristic value from the column according to which a condition is set in the given node with the threshold value. -3. Depending on the result, choose the tree branch along which you will proceed (`true_branch` or `false_branch`). -4. Repeat these actions recursively until the result will be a class label (a leaf node). - - -To see the results of your code, add the following lines -to the `main` block in `task.py` and run it: -```python -print(f'Class prediction for object X[0]: {tree.predict(X[0])}\n') -``` -The variables required for this code to function were introduced in previous steps. If you haven't worked with -`task.py` yet, ensure they are properly defined. - +
-A decision tree predicts a class by **moving from the root to a leaf**. -If the current element is already a class label (not a `Node`), the traversal is finished. +If sub_tree is not a Node, return it — it already represents the class label. ```python if not isinstance(sub_tree, Node): return sub_tree ``` -
+2. Compare the characteristic value from the column according to which a condition is set in the given node with the threshold value. +
-Each node checks one feature of the object being classified. -The node stores which feature to check in `sub_tree.column`. -So you need to take that value from `x`. +Each node checks one feature of the object being classified. The node stores which feature to check in `sub_tree.column`. +You need to take that value from `x`. ```python v = x[sub_tree.column] @@ -50,14 +37,11 @@ v = x[sub_tree.column]
-
- -Some features are numeric (for example, age or income). -In this case the node represents a **threshold condition**, such as: +3. Depending on the result, choose the tree branch along which you will proceed (`true_branch` or `false_branch`). -`age >= 30` +
-So if the feature value is numeric, compare it with the node’s threshold to decide which branch to follow. +For numeric features, the node applies a threshold (e.g., `age >= 30`), so compare the value with the threshold to choose the branch. ```python if isinstance(v, int) or isinstance(v, float): @@ -69,14 +53,10 @@ if isinstance(v, int) or isinstance(v, float):
-
- -Other features may be categorical (for example, color or country). -In this case the node checks **equality** with the stored value. +
-For example: `color == "red"` - -Choose the branch depending on whether the value matches the condition. +For categorical features, the node checks equality (e.g., `color == "red"`). +Choose the branch based on whether the value matches. ```python else: @@ -88,18 +68,22 @@ else:
-
- -After choosing the next branch, the classification is **not finished yet**. -You have only moved **one step down the tree**. +4. Repeat these actions recursively until the result will be a class label (a leaf node). -The selected branch may still be another node that asks the next question. -So you need to **repeat the same process** for the new subtree. +
-The easiest way to repeat the same logic is to **call the same function again** with the new branch. +After choosing a branch, you are not done — there may still be another node. +Apply the same logic again by calling the function on the new branch until you reach a leaf (class label). ```python return self.classify_subtree(x, branch) ``` -The recursion continues until a leaf node (class label) is reached, which is then returned. -
\ No newline at end of file +
+ +To see the results of your code, add the following lines +to the `main` block in `task.py` and run it: +```python +print(f'Class prediction for object X[0]: {tree.predict(X[0])}\n') +``` +The variables required for this code to function were introduced in previous steps. If you haven't worked with +`task.py` yet, ensure they are properly defined. From 6a081a878ff0f34936c9401077137c91fdc750d7 Mon Sep 17 00:00:00 2001 From: "aleksandra.valerianova" Date: Tue, 17 Mar 2026 17:52:45 +0200 Subject: [PATCH 5/7] Replace the link to the insecure connection --- Horror Trees/Conclusion/task.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Horror Trees/Conclusion/task.md b/Horror Trees/Conclusion/task.md index 9fb9ba8..52c651d 100644 --- a/Horror Trees/Conclusion/task.md +++ b/Horror Trees/Conclusion/task.md @@ -40,5 +40,5 @@ To see the results of the algorithm's work, run the code in the `halloween.py` f see an image `tree.jpg` in the list of files in Course View – it is our decision tree! #### Additional information links: -1) An [example](https://iq.opengenus.org/id3-algorithm/#:~:text=ID3%20algorithm%2C%20stands%20for%20Iterative,or%20minimum%20Entropy%20(H)) of using the ID3 algorithm in weather forecasts. +1) An [example](https://discourse.opengenus.org/t/using-id3-algorithm-to-build-a-decision-tree-to-predict-the-weather/3343) of using the ID3 algorithm in weather forecasts. 2) An [article](https://en.wikipedia.org/wiki/ID3_algorithm#cite_ref-2) in Wikipedia on ID3. From 0423e1bb3ddcdd917d9324401931240dd24ccdac Mon Sep 17 00:00:00 2001 From: "aleksandra.valerianova" Date: Fri, 20 Mar 2026 15:44:32 +0200 Subject: [PATCH 6/7] Short hints with solutions for Bayes Guards and Iris Network lessons --- Bayes Guards SMS/Predict/task.md | 116 ++++++++----------------- Iris Network/Backpropagation/task.md | 33 ++++++- Iris Network/Train and Predict/task.md | 23 ++++- 3 files changed, 85 insertions(+), 87 deletions(-) diff --git a/Bayes Guards SMS/Predict/task.md b/Bayes Guards SMS/Predict/task.md index c9f74c8..a684bcf 100644 --- a/Bayes Guards SMS/Predict/task.md +++ b/Bayes Guards SMS/Predict/task.md @@ -33,130 +33,82 @@ delete the `pass` operator. help of the `split_by_words()` function. - In each message, find a set of unique words and create a vector of zeros of the same size. -- For each unique word from the list, find a correspondence in the dictionary; if you find it, - write its index to the vector created in the previous step, if not – write an index - equal to the dictionary length. All such words have the same - probability. -- Calculate `log_likelihood` by applying the `np.log()` function to the slice of the `likelihood` array - obtained with the help of `index_array`; thus, the array will contain the probabilities of only - those words that occur in our sentence. -- Use the above formula to calculate the most probable class for this message. -- Return the list of most probable classes of all messages in the input array. - - -Then, implement the `score` method, which passes the testing sample through the algorithm, compares the received -class labels with the real ons and returns the proportion of correctly classified objects. - -
-Posterior probabilities for each class are calculated as the sum of the prior probability logarithm and the summarized logarithms of probabilities for the words from -log_likelihood -positioned along one axis, i.e., separately for each class. -
- -
-After finding the posterior probabilities for classes, you need to determine which -one is the largest among them and choose a class corresponding to it from unique_classes. Here, -you may use the numpy.argmax function. -
-For every message, you need to convert its words into dictionary indices. -Create an array that will store the index of each unique word. +Create a zero-initialized array to store the indices of each unique word. ```python index_array = np.zeros(unique.shape, dtype=np.int64) ``` -
-
+- For each unique word from the list, find a correspondence in the dictionary; if you find it, + write its index to the vector created in the previous step, if not – write an index + equal to the dictionary length. All such words have the same + probability. -Each word must be mapped to its index in `self.dictionary.` +
-If the word exists in the dictionary, use its stored index. -If it does not exist, use the special index for unknown words: `self.dict_size` (length of the dictionary). +For each word, assign its dictionary index to `word_index`; if it’s not found, set `word_index = dict_size` ```python -for i, word in enumerate(unique): word_index = self.dictionary[word] if word in self.dictionary else self.dict_size ``` + **Why do this?** Because `self.likelihood` stores word probabilities by index, not by the word itself.
-
- -`self.likelihood` stores the probabilities of words for each class. - -Its shape is: -`number_of_classes * (dictionary_size + 1)` - - -- each **row** corresponds to a class -- each **column** corresponds to a word index in the dictionary +- Calculate `log_likelihood` by applying the `np.log()` function to the slice of the `likelihood` array + obtained with the help of `index_array`; thus, the array will contain the probabilities of only + those words that occur in our sentence. -For example, `self.likelihood[c, w]` represents the probability of word `w` -appearing in class `c`. +
-To compute the likelihood of the current message, select only the columns -that correspond to the words in the message and apply the logarithm: +`self.likelihood[c, w]` is the probability of word `w` in class `c`. +Select probabilities for message words and take log: ```python log_likelihood = np.log(self.likelihood[:, index_array]) ```
-
- -At this point, `log_likelihood` already contains the log-probabilities of the words from the current message for **each class**. - -Now you need to get one final score for every class. +- Compute the posterior score for each class. -To do this you need to combine two things: -
    -
  • how typical the words of the message are for that class
  • -
  • how common that class is in the training data
  • -
- -The first part is obtained from log_likelihood. -The second part is the logarithm of the prior probability of the class stored in classes_prior. +
+Posterior score shows how likely each class is for the given message. +Compute it using the formula above in log form: sum log-probabilities of words (axis=1) and add the log prior. +
-Therefore, add the log prior probability to the summed log-likelihood values: +
```python posterior = np.log(self.classes_prior) + np.sum(log_likelihood, axis=1) ```
-
-At this point, posterior contains one score for each class. -These scores represent how likely the message belongs to each class. +- Select the class with the highest score for each message. -Your task is to select the class with the highest score. - -First, find the index of the largest value in the posterior array. -You can use numpy.argmax for this — it returns the position of the maximum element. - -Then use this index to retrieve the corresponding class label from self.unique_classes. - -
predicted = self.unique_classes[np.argmax(posterior)]
- -This selects the class with the highest posterior score. +
+After finding the posterior probabilities for classes, you need to determine which +one is the largest among them and choose a class corresponding to it from unique_classes. Here, +you may use the numpy.argmax function.
-
-The classifier already knows how to predict class labels using the predict method. -However, we also need a simple way to check how well the model performs. +
-The score method does this by comparing the predicted labels with the true labels and computing the fraction of matches. +```python +predicted = self.unique_classes[np.argmax(posterior)] +``` +
-You can obtain the predicted labels by calling predict. +Then, implement the `score` method, which passes the testing sample through the algorithm, compares the received +class labels with the real ones and returns the proportion of correctly classified objects. -When two arrays are compared with ==, NumPy produces a boolean array where each position shows whether the prediction is correct. -Summing this array gives the number of correct predictions. -To obtain the required value, divide this number by the total number of objects. +
+Use predict to get labels, compare them with true labels y, and return the fraction of matches:
return np.sum(self.predict(X) == y) / len(y)
diff --git a/Iris Network/Backpropagation/task.md b/Iris Network/Backpropagation/task.md index 21080af..d816014 100644 --- a/Iris Network/Backpropagation/task.md +++ b/Iris Network/Backpropagation/task.md @@ -70,9 +70,40 @@ In the `network.py` file, implement only the `backward` method of the `NN` class
  • Calculate the error for the output layer (delta_l2) as the difference between the network results (output) and the real class labels (y) multiplied elementwise by the derivative of the activation function for output ($\delta_{o}$ formula).
  • -
  • Calculate the error for the hidden layer (delta_l1) as the product of input layer error matrices and the weights w2 multiplied elementwise by the derivative of the activation function wrt the output data of the hidden layer (layer1) ($\delta_{h}$ formula).
  • + +
    + +```python +delta_l2 = (y - output) * sigmoid_derivative(output) +``` +
    + +
  • Calculate the error for the hidden layer (delta_l1) as the product of output layer error matrices and the weights w2 multiplied elementwise by the derivative of the activation function wrt the output data of the hidden layer (layer1) ($\delta_{h}$ formula).
  • + +
    + +```python +delta_l1 = np.dot(delta_l2, self.w2.T) * sigmoid_derivative(self.layer1) +``` +
    +
  • Adjust the weight coefficients of the output layer (w2) by calculating the vector product of the hidden layer (layer1) and the output layer error (delta_l2) multiplied elementwise by the learning rate (formula 3).
  • + +
    + +```python +self.w2 += (np.dot(self.layer1.T, delta_l2) * learning_rate) +``` +
    +
  • Adjust the weight coefficients of the hidden layer (w1) by calculating the vector product of the input layer (X) and the hidden layer error (delta_l1), multiplied elementwise by the learning rate (formula 3).
  • + +
    + +```python +self.w1 += (np.dot(X.T, delta_l1) * learning_rate) +``` +
Before you start, delete the `pass` operator and uncomment all lines that are not task commentaries. diff --git a/Iris Network/Train and Predict/task.md b/Iris Network/Train and Predict/task.md index 9e0cc19..b9c8106 100644 --- a/Iris Network/Train and Predict/task.md +++ b/Iris Network/Train and Predict/task.md @@ -5,13 +5,28 @@ The process of setting up a neural network involves successive implementation of In the `network.py` file, implement the `train` method of the `NN` class. Besides data, it takes the `n_iter` parameter, which sets the necessary number of iterations. The method should call two other (previously implemented) methods in the right order. It does not return anything. +
+On each iteration, compute predictions with feedforward and update the model using backward. + +```python + for itr in range(n_iter): + l2 = self.feedforward(X) + self.backward(X, y, l2) +``` +
+ Augment the implementation by the `predict` method, which passes all objects from the `X` matrix through the trained neural network. -Before you start, delete the `pass` operator and uncomment all lines that are not task commentaries. +
The predict method is a part of the interface of a program the neural network is expected to include, +so we will implement it despite the fact that it just calls the feedforward method. -
The predict method is a part of the interface of a program the neural network is expected to include, so we will implement it -despite the fact that it just calls the feedforward method. It's a lucky coincidence – in other cases, there might be -something else.
+```python +return self.feedforward(X) +``` +It's a lucky coincidence – in other cases, there might be something more complex. +
+ +Before you start, delete the `pass` operator and uncomment all lines that are not task commentaries. To see the results of your code in this step, add the following lines to the `main` block in `task.py`: From e86fd56bb6607ef469a53cf84bdb6b8602ff230e Mon Sep 17 00:00:00 2001 From: "aleksandra.valerianova" Date: Fri, 27 Mar 2026 17:15:51 +0200 Subject: [PATCH 7/7] Text changes applied --- Bayes Guards SMS/Predict/task.md | 22 +++++++++++----------- Horror Trees/Information Gain/task.md | 4 ++-- Horror Trees/Predict/task.md | 17 +++++++++-------- Iris Network/Backpropagation/task.md | 4 +++- Iris Network/Train and Predict/task.md | 11 ++++++----- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Bayes Guards SMS/Predict/task.md b/Bayes Guards SMS/Predict/task.md index a684bcf..ab8ef56 100644 --- a/Bayes Guards SMS/Predict/task.md +++ b/Bayes Guards SMS/Predict/task.md @@ -35,7 +35,7 @@ delete the `pass` operator. the same size.
-Create a zero-initialized array to store the indices of each unique word. +Create a zero-initialized array to store the index of each unique word. ```python index_array = np.zeros(unique.shape, dtype=np.int64) @@ -49,7 +49,7 @@ index_array = np.zeros(unique.shape, dtype=np.int64)
-For each word, assign its dictionary index to `word_index`; if it’s not found, set `word_index = dict_size` +For each word, assign its dictionary index to `word_index`; if the word is not found, set `word_index` to `dict_size`. ```python word_index = self.dictionary[word] if word in self.dictionary else self.dict_size @@ -57,7 +57,7 @@ For each word, assign its dictionary index to `word_index`; if it’s not found, **Why do this?** -Because `self.likelihood` stores word probabilities by index, not by the word itself. +Because `self.likelihood` stores word probabilities by index rather than the string itself.
@@ -67,8 +67,8 @@ Because `self.likelihood` stores word probabilities by index, not by the word it
-`self.likelihood[c, w]` is the probability of word `w` in class `c`. -Select probabilities for message words and take log: +`self.likelihood[c, w]` represents the probability of word `w` for class `c`. +Select the probabilities for the message words and calculate their logs: ```python log_likelihood = np.log(self.likelihood[:, index_array]) @@ -78,8 +78,8 @@ log_likelihood = np.log(self.likelihood[:, index_array]) - Compute the posterior score for each class.
-Posterior score shows how likely each class is for the given message. -Compute it using the formula above in log form: sum log-probabilities of words (axis=1) and add the log prior. +The posterior score indicates the probability of each class given the message. +Compute it by summing the log-probabilities of the words (across axis=1) and adding the log prior.
@@ -89,7 +89,7 @@ posterior = np.log(self.classes_prior) + np.sum(log_likelihood, axis=1) ```
-- Select the class with the highest score for each message. +- Identify the class with the highest score for each message.
After finding the posterior probabilities for classes, you need to determine which @@ -104,11 +104,11 @@ predicted = self.unique_classes[np.argmax(posterior)] ```
-Then, implement the `score` method, which passes the testing sample through the algorithm, compares the received -class labels with the real ones and returns the proportion of correctly classified objects. +Then, implement the `score` method, which passes the test samples through the algorithm, compares the predicted +class labels with the true labels, and returns the proportion of correctly classified objects.
-Use predict to get labels, compare them with true labels y, and return the fraction of matches: +Use predict to generate labels, compare them with the true labels y, and return the fraction of matches:
return np.sum(self.predict(X) == y) / len(y)
diff --git a/Horror Trees/Information Gain/task.md b/Horror Trees/Information Gain/task.md index aeb3e30..5cea6a3 100644 --- a/Horror Trees/Information Gain/task.md +++ b/Horror Trees/Information Gain/task.md @@ -24,14 +24,14 @@ in the previous step.
-Just use `divide` method of the `Predicate` class to divide the sample into two subsets. +Use the divide `method` from the `Predicate` class to split the sample into two subsets: `X1, y1, X2, y2 = self.divide(X, y)`
-Each subset contributes to entropy proportionally to its size, so we compute the fraction of samples in the first subset: +Each subset contributes to the total entropy proportionally to its size. Compute the fraction of samples in the first subset as follows: `p = float(len(X1)) / len(X)` diff --git a/Horror Trees/Predict/task.md b/Horror Trees/Predict/task.md index 3f3cd34..5190e91 100644 --- a/Horror Trees/Predict/task.md +++ b/Horror Trees/Predict/task.md @@ -16,7 +16,7 @@ In `classify_subtree`, you need to:
-If sub_tree is not a Node, return it — it already represents the class label. +If `sub_tree` is not a `Node`, return it; it already represents the class label. ```python if not isinstance(sub_tree, Node): @@ -28,8 +28,8 @@ if not isinstance(sub_tree, Node):
-Each node checks one feature of the object being classified. The node stores which feature to check in `sub_tree.column`. -You need to take that value from `x`. +Each node evaluates a specific feature of the object being classified. The index of the feature to check is stored in `sub_tree.column`. +You need to extract the corresponding value from `x`. ```python v = x[sub_tree.column] @@ -41,7 +41,8 @@ v = x[sub_tree.column]
-For numeric features, the node applies a threshold (e.g., `age >= 30`), so compare the value with the threshold to choose the branch. +For numeric features, the node evaluates a threshold (e.g., `age >= 30`). +Compare the feature value against this threshold to determine which branch to follow. ```python if isinstance(v, int) or isinstance(v, float): @@ -55,8 +56,8 @@ if isinstance(v, int) or isinstance(v, float):
-For categorical features, the node checks equality (e.g., `color == "red"`). -Choose the branch based on whether the value matches. +For categorical features, the node evaluates an equality condition (e.g., `color == "red"`). +Determine the next branch based on whether the feature value matches this criterion. ```python else: @@ -72,8 +73,8 @@ else:
-After choosing a branch, you are not done — there may still be another node. -Apply the same logic again by calling the function on the new branch until you reach a leaf (class label). +Choosing a branch is only the first step – you may encounter another node. +Apply the same logic again by calling the function on the selected branch until you reach a leaf (the final class label). ```python return self.classify_subtree(x, branch) diff --git a/Iris Network/Backpropagation/task.md b/Iris Network/Backpropagation/task.md index d816014..4423ca0 100644 --- a/Iris Network/Backpropagation/task.md +++ b/Iris Network/Backpropagation/task.md @@ -78,7 +78,9 @@ delta_l2 = (y - output) * sigmoid_derivative(output) ```
-
  • Calculate the error for the hidden layer (delta_l1) as the product of output layer error matrices and the weights w2 multiplied elementwise by the derivative of the activation function wrt the output data of the hidden layer (layer1) ($\delta_{h}$ formula).
  • +
  • Calculate the error for the hidden layer (delta_l1) by taking the product of the output layer error +and the transpose of the weight matrix w2, then multiplying element-wise by the derivative +of the activation function with respect to the hidden layer's output (layer1) ($\delta_{h}$ formula).
  • diff --git a/Iris Network/Train and Predict/task.md b/Iris Network/Train and Predict/task.md index b9c8106..c1d2b92 100644 --- a/Iris Network/Train and Predict/task.md +++ b/Iris Network/Train and Predict/task.md @@ -6,7 +6,7 @@ In the `network.py` file, implement the `train` method of the `NN` class. Beside the necessary number of iterations. The method should call two other (previously implemented) methods in the right order. It does not return anything.
    -On each iteration, compute predictions with feedforward and update the model using backward. +On each iteration, generate predictions via feedforward and update the model's parameters using backward propagation. ```python for itr in range(n_iter): @@ -17,16 +17,17 @@ On each iteration, compute predictions with feedforward and update Augment the implementation by the `predict` method, which passes all objects from the `X` matrix through the trained neural network. -
    The predict method is a part of the interface of a program the neural network is expected to include, -so we will implement it despite the fact that it just calls the feedforward method. +
    +The predict method is a required part of the neural network's interface. +We will implement it here, even though it simply acts as a wrapper for the feedforward method. ```python return self.feedforward(X) ``` -It's a lucky coincidence – in other cases, there might be something more complex. +While this case is straightforward, other scenarios may require a more complex implementation.
    -Before you start, delete the `pass` operator and uncomment all lines that are not task commentaries. +Before you begin, delete the `pass` statement and uncomment all lines that are not task-related comments. To see the results of your code in this step, add the following lines to the `main` block in `task.py`: