From 407f8afaf39bdd723a5e84c156b44ea08af32df9 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Tue, 30 Mar 2021 00:50:18 +0800 Subject: [PATCH 01/28] feat: _get_k_nearest_neighbors --- .../submission/18307130003/tester_demo.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 assignment-1/submission/18307130003/tester_demo.py diff --git a/assignment-1/submission/18307130003/tester_demo.py b/assignment-1/submission/18307130003/tester_demo.py new file mode 100644 index 0000000..6fe1511 --- /dev/null +++ b/assignment-1/submission/18307130003/tester_demo.py @@ -0,0 +1,24 @@ +import numpy as np + +from source import KNN + +train_data = np.array([ + [1, 2, 3, 4], + [4, 2, 3, 1], + [12, 12, 13, 14], + [14, 12, 13, 11], + [12, 14, 15, 16], +]) +train_label = np.array([0, 0, 1, 1, 1]) + +test_data = np.array([ + [3, 4, 4, 2], + [18, 14, 15, 16], +]) +test_label = np.array([0, 1]) + +model = KNN() +model.fit(train_data, train_label) +res = model.predict(test_data) + +print("acc =", np.mean(np.equal(res, test_label))) -- Gitee From a70a664d7058e896d9b6238a8f419379e30d1096 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 1 Apr 2021 16:10:17 +0800 Subject: [PATCH 02/28] feat: _get_most_common_label chore: fix comments and modify parameter names --- assignment-1/submission/18307130003/source.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/assignment-1/submission/18307130003/source.py b/assignment-1/submission/18307130003/source.py index 3676e4e..a672bc8 100644 --- a/assignment-1/submission/18307130003/source.py +++ b/assignment-1/submission/18307130003/source.py @@ -42,7 +42,12 @@ class KNN: :param `dataset`: which dataset to use ''' - def _distance(p1: np.ndarray, p2: np.ndarray, mode: int = 2) -> float: + if dataset == Dataset.TRAIN_SET: + data = self.train_data + else: + data = self.test_data + + def _distance(p1_i: int, p2_i: int, mode: int = 2) -> float: ''' Get the distance between two points. -- Gitee From b9afc8f62b4a449801741b69b8d7c8e413ec71f0 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 1 Apr 2021 21:57:28 +0800 Subject: [PATCH 03/28] feat: fit, predict fix: change .size to .shape[0] --- assignment-1/submission/18307130003/source.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assignment-1/submission/18307130003/source.py b/assignment-1/submission/18307130003/source.py index a672bc8..aed1027 100644 --- a/assignment-1/submission/18307130003/source.py +++ b/assignment-1/submission/18307130003/source.py @@ -44,6 +44,8 @@ class KNN: if dataset == Dataset.TRAIN_SET: data = self.train_data + elif dataset == Dataset.DEV_SET: + data = self.dev_data else: data = self.test_data -- Gitee From dbc19c6f2aac30265aeb8677f32e71100a7fd053 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 1 Apr 2021 22:25:11 +0800 Subject: [PATCH 04/28] fix: fixed all issues to get the program worked --- assignment-1/submission/18307130003/source.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/assignment-1/submission/18307130003/source.py b/assignment-1/submission/18307130003/source.py index aed1027..1c472cf 100644 --- a/assignment-1/submission/18307130003/source.py +++ b/assignment-1/submission/18307130003/source.py @@ -42,14 +42,7 @@ class KNN: :param `dataset`: which dataset to use ''' - if dataset == Dataset.TRAIN_SET: - data = self.train_data - elif dataset == Dataset.DEV_SET: - data = self.dev_data - else: - data = self.test_data - - def _distance(p1_i: int, p2_i: int, mode: int = 2) -> float: + def _distance(p1: np.ndarray, p2: np.ndarray, mode: int = 2) -> float: ''' Get the distance between two points. @@ -156,6 +149,8 @@ class KNN: print(f'best k = {self.k}\n') + print("\n") + def predict(self, test_data: np.ndarray) -> np.ndarray: ''' Predict the label of a point using our model. -- Gitee From 58d5e433dc2e1f8c50ec095d9038e6ac416a2458 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 2 Apr 2021 00:01:37 +0800 Subject: [PATCH 05/28] feat: done doc: done --- assignment-1/submission/18307130003/README.md | 502 ++++++------------ .../submission/18307130003/img/test.png | Bin 0 -> 25961 bytes .../submission/18307130003/img/train.png | Bin 0 -> 38778 bytes assignment-1/submission/18307130003/source.py | 2 +- 4 files changed, 155 insertions(+), 349 deletions(-) create mode 100644 assignment-1/submission/18307130003/img/test.png create mode 100644 assignment-1/submission/18307130003/img/train.png diff --git a/assignment-1/submission/18307130003/README.md b/assignment-1/submission/18307130003/README.md index 2bd04be..9e5140a 100644 --- a/assignment-1/submission/18307130003/README.md +++ b/assignment-1/submission/18307130003/README.md @@ -1,179 +1,169 @@ # 实验报告 -## 目录 - -[TOC] - ## KNN 模型实现 -KNN 算法的主要思路是,根据当前点 `base_p` 最近的 `k` 个邻居 `p` 的标签,选择其中出现频率最高的标签,作为 `base_p` 标签的预测结果。 - -具体来说,我们使用函数 `_distance` 获取两点间的距离。 - ```python {.line-numbers} -def _distance(p1: np.ndarray, p2: np.ndarray, mode: int = 2) -> float: +class KNN: ''' - Get the distance between two points. - - :param `p1`: the first point - :param `p2`: the second point - :param `mode`: which exponent to use when calculating distance, \ - using `2` by default for Euclidean distance + k-Nearest Neighbors algorithm, which predicts the class of a data point + according to the most common class among its `k` nearest neighbors. ''' - assert p1.shape == p2.shape, ( - '_distance: dimensions not match for ' - f'{p1.shape} and {p2.shape}' - ) - return np.linalg.norm(p1 - p2, ord=mode) -``` + def __init__(self) -> None: + ''' + Initialize our KNN model. + ''' -这里我们使用了 `numpy` 库提供的 `np.linalg.norm` 方法来获取两点间的距离。特别地,当参数 `ord` 为 `2` 时,即采用 Euclidean 距离。我们这里使用 `2` 作为默认参数。 + self.k: int = 1 + self.train_data: np.ndarray = None + self.train_label: np.ndarray = None + self.dev_data: np.ndarray = None + self.dev_label: np.ndarray = None + self.test_data: np.ndarray = None -接下来,我们维护一个大小为 `k` 的最小堆,来得到最近的 `k` 个邻居。思想就是 top k 问题的经典算法。 + def _get_k_nearest_neighbors( + self, k: int, base_p: np.ndarray, dataset: int = Dataset.TRAIN_SET + ) -> List[int]: + ''' + Get k nearest neighbors of a point from dataset. Each point (except the + base point) is denoted by its index in the dataset. -```python {.line-numbers} -def _get_k_nearest_neighbors( - self, k: int, base_p: np.ndarray, dataset: int = Dataset.TRAIN_SET -) -> List[int]: - ''' - Get k nearest neighbors of a point from dataset. Each point (except the - base point) is denoted by its index in the dataset. + :param `base_p`: the base point + :param `dataset`: which dataset to use + ''' - :param `base_p`: the base point - :param `dataset`: which dataset to use - ''' + def _distance(p1: np.ndarray, p2: np.ndarray, mode: int = 2) -> float: + ''' + Get the distance between two points. - if dataset == Dataset.TRAIN_SET: - data = self.train_data - elif dataset == Dataset.DEV_SET: - data = self.dev_data - else: - data = self.test_data - - # Use a min heap of size k to get the k nearest neighbors - heap: List[Tuple[float, np.ndarray]] = [] - for p_i in range(data.shape[0]): - dist: float = _distance(base_p, data[p_i]) - if (len(heap) < k): - heappush(heap, (-dist, p_i)) - else: - heappushpop(heap, (-dist, p_i)) + :param `p1`: the first point + :param `p2`: the second point + :param `mode`: which exponent to use when calculating distance, \ + using `2` by default for Euclidean distance + ''' - # Return the indices of the k points in the dataset - return [item[1] for item in heap] -``` + assert p1.shape == p2.shape, ( + '_distance: dimensions not match for ' + f'{p1.shape} and {p2.shape}' + ) + return np.linalg.norm(p1 - p2, ord=mode) -最后我们对这 `k` 个邻居的标签分别进行计数,选择其中出现次数最多的标签作为预测结果。 + if dataset == Dataset.TRAIN_SET: + data = self.train_data + elif dataset == Dataset.DEV_SET: + data = self.dev_data + else: + data = self.test_data + + # Use a min heap of size k to get the k nearest neighbors + heap: List[Tuple[float, np.ndarray]] = [] + for p_i in range(data.shape[0]): + dist: float = _distance(base_p, data[p_i]) + if (len(heap) < k): + heappush(heap, (-dist, p_i)) + else: + heappushpop(heap, (-dist, p_i)) + + # Return the indices of the k points in the dataset + return [item[1] for item in heap] + + def _get_most_common_label( + self, labels_i: List[int], dataset: int = Dataset.TRAIN_SET + ) -> int: + ''' + Get the most common label in given labels. Each label is denoted by + its data point's index in the dataset. -```python {.line-numbers} -def _get_most_common_label( - self, labels_i: List[int], dataset: int = Dataset.TRAIN_SET -) -> int: - ''' - Get the most common label in given labels. Each label is denoted by - its data point's index in the dataset. + :param `labels_i`: the indices of given labels + :param `dataset`: which dataset to use + ''' - :param `labels_i`: the indices of given labels - :param `dataset`: which dataset to use - ''' + if dataset == Dataset.TRAIN_SET: + all_labels = self.train_label + else: + all_labels = self.dev_label - if dataset == Dataset.TRAIN_SET: - all_labels = self.train_label - else: - all_labels = self.dev_label + labels: List[int] = [all_labels[i] for i in labels_i] + return max(set(labels), key=labels.count) - labels: List[int] = [all_labels[i] for i in labels_i] - return max(set(labels), key=labels.count) -``` + def fit(self, train_data: np.ndarray, train_label: np.ndarray) -> None: + ''' + Train the model using a training set with labels. -训练模型时,我们先将数据集打乱,然后将其中的 75% 作为训练集 `train_data`,剩下 25% 作为验证集 `dev_data`,然后使用 KNN 算法进行训练。我们选择不同的 `k` 值,通过比较验证集 `dev_data` 的预测结果和其实际标签 `dev_label`,得到每个 `k` 值所对应的预测准确率 `accuracy`。最终,我们选择准确率最高的 `k` 值作为我们将在测试集 `test_data` 上使用的参数 `k`。 + :param `train_data`: training set + :param `train_label`: provided labels for data in training set + ''' -```python {.line-numbers} -def fit(self, train_data: np.ndarray, train_label: np.ndarray) -> None: - ''' - Train the model using a training set with labels. + # Shuffle the dataset with labels + assert train_data.shape[0] == train_label.shape[0], ( + 'fit: data size not match for ' + f'{train_data.shape[0]} and {train_label.shape[0]}' + ) + shuffled_i = np.random.permutation(train_data.shape[0]) + shuffled_data: np.ndarray = train_data[shuffled_i] + shuffled_label: np.ndarray = train_label[shuffled_i] + + # Separate training set and development set (for validation) + train_ratio: float = 0.75 + train_size: int = floor(shuffled_data.shape[0] * train_ratio) + self.train_data = shuffled_data[:train_size] + self.train_label = shuffled_label[:train_size] + self.dev_data = shuffled_data[train_size:] + self.dev_label = shuffled_label[train_size:] + + print('=== Training ===') + + # Compare the predicted and expected results, calculate the accuracy + # for each parameter k, and find out the best k for prediction. + k_threshold: int = train_size if train_size < 20 else 20 + accuracy_table: List[float] = [0.0] + max_accuracy: float = 0.0 + + for k in range(1, k_threshold): + predicted_labels: List[int] = [] + for p in self.dev_data: + k_nearest_neighbors: List[int] = self._get_k_nearest_neighbors( + k, p, Dataset.TRAIN_SET + ) + predicted_label: int = self._get_most_common_label( + k_nearest_neighbors, Dataset.TRAIN_SET + ) + predicted_labels.append(predicted_label) + prediction: np.ndarray = np.array(predicted_labels) + + accuracy: float = np.mean(np.equal(prediction, self.dev_label)) + accuracy_table.append(accuracy) + print(f'k = {k}, train_acc = {accuracy * 100} %') + if accuracy > max_accuracy: + max_accuracy, self.k = accuracy, k + + print(f'best k = {self.k}\n') + + def predict(self, test_data: np.ndarray) -> np.ndarray: + ''' + Predict the label of a point using our model. - :param `train_data`: training set - :param `train_label`: provided labels for data in training set - ''' + :param `test_data`: testing set + ''' + + self.test_data = test_data - # Shuffle the dataset with labels - assert train_data.shape[0] == train_label.shape[0], ( - 'fit: data size not match for ' - f'{train_data.shape[0]} and {train_label.shape[0]}' - ) - shuffled_i = np.random.permutation(train_data.shape[0]) - shuffled_data: np.ndarray = train_data[shuffled_i] - shuffled_label: np.ndarray = train_label[shuffled_i] - - # Separate training set and development set (for validation) - train_ratio: float = 0.75 - train_size: int = floor(shuffled_data.shape[0] * train_ratio) - self.train_data = shuffled_data[:train_size] - self.train_label = shuffled_label[:train_size] - self.dev_data = shuffled_data[train_size:] - self.dev_label = shuffled_label[train_size:] - - print('=== Training ===') - - # Compare the predicted and expected results, calculate the accuracy - # for each parameter k, and find out the best k for prediction. - k_threshold: int = train_size if train_size < 20 else 20 - accuracy_table: List[float] = [0.0] - max_accuracy: float = 0.0 - - for k in range(1, k_threshold): predicted_labels: List[int] = [] - for p in self.dev_data: + for p in self.test_data: k_nearest_neighbors: List[int] = self._get_k_nearest_neighbors( - k, p, Dataset.TRAIN_SET + self.k, p, Dataset.TRAIN_SET ) predicted_label: int = self._get_most_common_label( k_nearest_neighbors, Dataset.TRAIN_SET ) predicted_labels.append(predicted_label) prediction: np.ndarray = np.array(predicted_labels) - - accuracy: float = np.mean(np.equal(prediction, self.dev_label)) - accuracy_table.append(accuracy) - print(f'k = {k}, train_acc = {accuracy * 100} %') - if accuracy > max_accuracy: - max_accuracy, self.k = accuracy, k - - print(f'best k = {self.k}\n') -``` - -对测试集进行预测时,我们就使用之前得到的最优的参数 `k` 进行预测,同样使用 KNN 算法。 - -```python {.line-numbers} -def predict(self, test_data: np.ndarray) -> np.ndarray: - ''' - Predict the label of a point using our model. - - :param `test_data`: testing set - ''' - - self.test_data = test_data - - print('=== Predicting ===') - - predicted_labels: List[int] = [] - for p in self.test_data: - k_nearest_neighbors: List[int] = self._get_k_nearest_neighbors( - self.k, p, Dataset.TRAIN_SET - ) - predicted_label: int = self._get_most_common_label( - k_nearest_neighbors, Dataset.TRAIN_SET - ) - predicted_labels.append(predicted_label) - prediction: np.ndarray = np.array(predicted_labels) - return prediction + return prediction ``` -## 生成数据 +## 实验探究部分 -我们使用不同的参数生成数据集,并保存到文件 `data.npy`。这里由于时间有限,为了方便起见,我们直接在函数 `generate` 的 `parameters` 变量中对相应参数进行修改。 +生成数据集,并保存到文件: ```python {.line-numbers} def generate() -> None: @@ -190,7 +180,7 @@ def generate() -> None: def _generate_with_parameters(param: Parameter) -> np.ndarray: ''' - Generate a dataset using given parameters. + Generate a dataset using given parameters :param `param`: a tuple of `mean`, `cov`, `size` `mean`: the mean of the dataset @@ -248,34 +238,27 @@ def generate() -> None: )) ``` -为了直观起见,我们提供了函数 `display` 用于将当前使用的数据集可视化,并将图片保存到 `./img` 目录下。 +作图: ```python {.line-numbers} -def display(data: np.ndarray, label: np.ndarray, name: str) -> None: +def display(data, label, name): ''' - Visualize dataset with labels using `matplotlib.pyplot`. - - :param `data`: dataset - :param `label`: labels for data in the dataset - :param `name`: file name when saving to file + Visualize data and labels using `matplotlib.pyplot`. ''' - datasets_with_label: List[List[np.ndarray]] = [[], [], []] - for i in range(data.shape[0]): - datasets_with_label[label[i]].append(data[i]) - - for dataset_with_label in datasets_with_label: - dataset_with_label_: np.ndarray = np.array(dataset_with_label) - plt.scatter(dataset_with_label_[:, 0], dataset_with_label_[:, 1]) + datas = [[], [], []] + for i in range(len(data)): + datas[label[i]].append(data[i]) + for each in datas: + each = np.array(each) + plt.scatter(each[:, 0], each[:, 1]) plt.savefig(f'img/{name}') plt.show() ``` ## 运行代码 -在当前目录下,我们可以使用以下参数执行代码 `source.py`,具体功能参见注释。 - ```bash # 训练模型及预测 python ./source.py g @@ -284,40 +267,9 @@ python ./source.py g python ./source.py d ``` -### 输出样例 - -```text -=== Training === -k = 1, train_acc = 67.5 % -k = 2, train_acc = 67.75 % -k = 3, train_acc = 70.5 % -k = 4, train_acc = 72.5 % -k = 5, train_acc = 72.75 % -k = 6, train_acc = 73.25 % -k = 7, train_acc = 74.25 % -k = 8, train_acc = 74.0 % -k = 9, train_acc = 75.75 % -k = 10, train_acc = 75.5 % -k = 11, train_acc = 75.75 % -k = 12, train_acc = 75.5 % -k = 13, train_acc = 75.75 % -k = 14, train_acc = 75.5 % -k = 15, train_acc = 75.5 % -k = 16, train_acc = 74.5 % -k = 17, train_acc = 75.0 % -k = 18, train_acc = 75.0 % -k = 19, train_acc = 75.25 % -best k = 9 - -=== Predicting === -k = 9, predict_acc = 71.5 % -``` - -## 实验探究 +## 数据集 -### 1. 实验 1 - -#### 1.1 参数 +数据生成使用的参数: ```python {.line-numbers} mean = (1, 2) @@ -337,27 +289,21 @@ cov = [[10, 5], [5, 10]] size = 1000 ``` -其中: - -- `mean` 表示数据集的均值 -- `cov` 表示数据集的协方差 -- `size` 表示数据集的大小 +其中,`mean` 表示数据集的均值、`cov` 表示数据集的协方差、`size` 表示数据集的大小。 -下同。 +### 训练集 -#### 1.2 数据集 +![训练集](./img/train.png) -训练集: +在训练时,我们随机选取 80% 作为训练集、20% 作为验证集。 -![训练集](./img/train_1.png) +### 测试集 -测试集: +![测试集](./img/test.png) -![测试集](./img/test_1.png) +## 参数 -#### 1.3 预测准确率 - -训练时使用的参数 `k` 及相应的准确率如下所示: +训练时使用的参数 `k` 及相应的准确率: ```text k = 1, train_acc = 95.75 % @@ -381,146 +327,6 @@ k = 18, train_acc = 96.75 % k = 19, train_acc = 97.0 % ``` -预测时使用的参数 `k` 及相应的准确率如下所示: - -```text -k = 3, predict_acc = 96.0 % -``` - -可见,对于此数据集,最优的参数 `k` 为 `3`,其对测试集的预测准确率为 96.0 %。 - -### 2. 实验 2 - -这次,我们调大数据集之间的距离,观察预测准确率的变化。 - -#### 2.1 参数 - -```python {.line-numbers} -mean = (-5, 2) -cov = [[73, 0], [0, 22]] -size = 800 -``` - -```python {.line-numbers} -mean = (30, -10) -cov = [[21.2, 0], [0, 32.1]] -size = 200 -``` - -```python {.line-numbers} -mean = (20, 40) -cov = [[10, 5], [5, 10]] -size = 1000 -``` - -#### 2.2 数据集 - -训练集: - -![训练集](./img/train_2.png) - -测试集: - -![测试集](./img/test_2.png) - -#### 2.3 预测准确率 - -训练时使用的参数 `k` 及相应的准确率如下所示: - -```text -k = 1, train_acc = 100.0 % -k = 2, train_acc = 100.0 % -k = 3, train_acc = 100.0 % -k = 4, train_acc = 100.0 % -k = 5, train_acc = 100.0 % -k = 6, train_acc = 100.0 % -k = 7, train_acc = 100.0 % -k = 8, train_acc = 100.0 % -k = 9, train_acc = 100.0 % -k = 10, train_acc = 100.0 % -k = 11, train_acc = 100.0 % -k = 12, train_acc = 100.0 % -k = 13, train_acc = 100.0 % -k = 14, train_acc = 100.0 % -k = 15, train_acc = 100.0 % -k = 16, train_acc = 100.0 % -k = 17, train_acc = 100.0 % -k = 18, train_acc = 100.0 % -k = 19, train_acc = 100.0 % -``` - -预测时使用的参数 `k` 及相应的准确率如下所示: - -```text -k = 1, predict_acc = 100.0 % -``` - -可见,对于不同标签区分度较大(即彼此间距离较远)的数据集,所有 `k` 的预测准确率均为 100.0 %。这说明 KNN 算法对于较分散的数据有着很高的准确率。 - -### 3. 实验 3 - -我们再试试减小数据集间的距离,观察预测准确率的变化。 - -#### 3.1 参数 - -```python {.line-numbers} -mean = (1, 2) -cov = [[73, 0], [0, 22]] -size = 800 -``` - -```python {.line-numbers} -mean = (3, -2) -cov = [[21.2, 0], [0, 32.1]] -size = 200 -``` - -```python {.line-numbers} -mean = (-5, 4) -cov = [[10, 5], [5, 10]] -size = 1000 -``` - -#### 3.2 数据集 - -训练集: - -![训练集](./img/train_3.png) - -测试集: - -![测试集](./img/test_3.png) - -#### 3.3 预测准确率 - -训练时使用的参数 `k` 及相应的准确率如下所示: - -```text -k = 1, train_acc = 65.0 % -k = 2, train_acc = 65.75 % -k = 3, train_acc = 69.25 % -k = 4, train_acc = 68.25 % -k = 5, train_acc = 72.5 % -k = 6, train_acc = 71.5 % -k = 7, train_acc = 73.75 % -k = 8, train_acc = 75.0 % -k = 9, train_acc = 76.0 % -k = 10, train_acc = 75.75 % -k = 11, train_acc = 76.0 % -k = 12, train_acc = 74.5 % -k = 13, train_acc = 75.25 % -k = 14, train_acc = 75.0 % -k = 15, train_acc = 74.75 % -k = 16, train_acc = 75.5 % -k = 17, train_acc = 75.0 % -k = 18, train_acc = 75.0 % -k = 19, train_acc = 74.5 % -``` - -预测时使用的参数 `k` 及相应的准确率如下所示: - -```text -k = 9, predict_acc = 76.0 % -``` +可见,对于此数据集,最优的参数 `k` 为 `3`。 -此时,最优的参数 `k` 为 `9`,其对测试集的预测准确率为 76.0 %。可见,当数据集间的区分度较低时,较高的 `k` 值有着相对较高的准确率。这是可以理解的,因为提高可参考的邻居数量可以尽可能地减少噪声的影响。 +相应的准确率为 96%。 diff --git a/assignment-1/submission/18307130003/img/test.png b/assignment-1/submission/18307130003/img/test.png new file mode 100644 index 0000000000000000000000000000000000000000..77b898ff8a239b7338b029c2b7e1f76c0fd149f2 GIT binary patch literal 25961 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 literal 0 HcmV?d00001 diff --git a/assignment-1/submission/18307130003/img/train.png b/assignment-1/submission/18307130003/img/train.png new file mode 100644 index 0000000000000000000000000000000000000000..12c56b0d5885a7f2c0a1658dbdf18e0911783088 GIT binary patch literal 38778 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 z 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 GWc>rO%;R$a literal 0 HcmV?d00001 diff --git a/assignment-1/submission/18307130003/source.py b/assignment-1/submission/18307130003/source.py index 1c472cf..e6bc70f 100644 --- a/assignment-1/submission/18307130003/source.py +++ b/assignment-1/submission/18307130003/source.py @@ -149,7 +149,7 @@ class KNN: print(f'best k = {self.k}\n') - print("\n") + print(f'best k = {self.k}\n') def predict(self, test_data: np.ndarray) -> np.ndarray: ''' -- Gitee From 42cfc5f0654916e737de1971f9ca02a013a3bb27 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 2 Apr 2021 01:04:01 +0800 Subject: [PATCH 06/28] doc: improve report --- assignment-1/submission/18307130003/README.md | 437 ++++++++++++------ .../submission/18307130003/img/test.png | Bin 25961 -> 0 bytes .../submission/18307130003/img/train.png | Bin 38778 -> 0 bytes .../submission/18307130003/tester_demo.py | 24 - 4 files changed, 292 insertions(+), 169 deletions(-) delete mode 100644 assignment-1/submission/18307130003/img/test.png delete mode 100644 assignment-1/submission/18307130003/img/train.png delete mode 100644 assignment-1/submission/18307130003/tester_demo.py diff --git a/assignment-1/submission/18307130003/README.md b/assignment-1/submission/18307130003/README.md index 9e5140a..21cd813 100644 --- a/assignment-1/submission/18307130003/README.md +++ b/assignment-1/submission/18307130003/README.md @@ -1,169 +1,177 @@ # 实验报告 +## 目录 + +[TOC] + ## KNN 模型实现 +KNN 算法的主要思路是,根据当前点 `base_p` 最近的 `k` 个邻居 `p` 的标签,选择其中出现频率最高的标签,作为 `base_p` 标签的预测结果。 + +具体来说,我们使用函数 `_distance` 获取两点间的距离。 + ```python {.line-numbers} -class KNN: +def _distance(p1: np.ndarray, p2: np.ndarray, mode: int = 2) -> float: ''' - k-Nearest Neighbors algorithm, which predicts the class of a data point - according to the most common class among its `k` nearest neighbors. - ''' - - def __init__(self) -> None: - ''' - Initialize our KNN model. - ''' + Get the distance between two points. - self.k: int = 1 - self.train_data: np.ndarray = None - self.train_label: np.ndarray = None - self.dev_data: np.ndarray = None - self.dev_label: np.ndarray = None - self.test_data: np.ndarray = None + :param `p1`: the first point + :param `p2`: the second point + :param `mode`: which exponent to use when calculating distance, \ + using `2` by default for Euclidean distance + ''' - def _get_k_nearest_neighbors( - self, k: int, base_p: np.ndarray, dataset: int = Dataset.TRAIN_SET - ) -> List[int]: - ''' - Get k nearest neighbors of a point from dataset. Each point (except the - base point) is denoted by its index in the dataset. + assert p1.shape == p2.shape, ( + '_distance: dimensions not match for ' + f'{p1.shape} and {p2.shape}' + ) + return np.linalg.norm(p1 - p2, ord=mode) +``` - :param `base_p`: the base point - :param `dataset`: which dataset to use - ''' +这里我们使用了 `numpy` 库提供的 `np.linalg.norm` 方法来获取两点间的距离。特别地,当参数 `ord` 为 `2` 时,即采用 Euclidean 距离。我们这里使用 `2` 作为默认参数。 - def _distance(p1: np.ndarray, p2: np.ndarray, mode: int = 2) -> float: - ''' - Get the distance between two points. +接下来,我们维护一个大小为 `k` 的最小堆,来得到最近的 `k` 个邻居。思想就是 top k 问题的经典算法。 - :param `p1`: the first point - :param `p2`: the second point - :param `mode`: which exponent to use when calculating distance, \ - using `2` by default for Euclidean distance - ''' +```python {.line-numbers} +def _get_k_nearest_neighbors( + self, k: int, base_p: np.ndarray, dataset: int = Dataset.TRAIN_SET +) -> List[int]: + ''' + Get k nearest neighbors of a point from dataset. Each point (except the + base point) is denoted by its index in the dataset. - assert p1.shape == p2.shape, ( - '_distance: dimensions not match for ' - f'{p1.shape} and {p2.shape}' - ) - return np.linalg.norm(p1 - p2, ord=mode) + :param `base_p`: the base point + :param `dataset`: which dataset to use + ''' - if dataset == Dataset.TRAIN_SET: - data = self.train_data - elif dataset == Dataset.DEV_SET: - data = self.dev_data + if dataset == Dataset.TRAIN_SET: + data = self.train_data + elif dataset == Dataset.DEV_SET: + data = self.dev_data + else: + data = self.test_data + + # Use a min heap of size k to get the k nearest neighbors + heap: List[Tuple[float, np.ndarray]] = [] + for p_i in range(data.shape[0]): + dist: float = _distance(base_p, data[p_i]) + if (len(heap) < k): + heappush(heap, (-dist, p_i)) else: - data = self.test_data - - # Use a min heap of size k to get the k nearest neighbors - heap: List[Tuple[float, np.ndarray]] = [] - for p_i in range(data.shape[0]): - dist: float = _distance(base_p, data[p_i]) - if (len(heap) < k): - heappush(heap, (-dist, p_i)) - else: - heappushpop(heap, (-dist, p_i)) - - # Return the indices of the k points in the dataset - return [item[1] for item in heap] - - def _get_most_common_label( - self, labels_i: List[int], dataset: int = Dataset.TRAIN_SET - ) -> int: - ''' - Get the most common label in given labels. Each label is denoted by - its data point's index in the dataset. + heappushpop(heap, (-dist, p_i)) - :param `labels_i`: the indices of given labels - :param `dataset`: which dataset to use - ''' + # Return the indices of the k points in the dataset + return [item[1] for item in heap] +``` - if dataset == Dataset.TRAIN_SET: - all_labels = self.train_label - else: - all_labels = self.dev_label +最后我们对这 k 个邻居的标签分别进行计数,选择其中出现次数最多的标签作为预测结果。 - labels: List[int] = [all_labels[i] for i in labels_i] - return max(set(labels), key=labels.count) +```python {.line-numbers} +def _get_most_common_label( + self, labels_i: List[int], dataset: int = Dataset.TRAIN_SET +) -> int: + ''' + Get the most common label in given labels. Each label is denoted by + its data point's index in the dataset. - def fit(self, train_data: np.ndarray, train_label: np.ndarray) -> None: - ''' - Train the model using a training set with labels. + :param `labels_i`: the indices of given labels + :param `dataset`: which dataset to use + ''' - :param `train_data`: training set - :param `train_label`: provided labels for data in training set - ''' + if dataset == Dataset.TRAIN_SET: + all_labels = self.train_label + else: + all_labels = self.dev_label - # Shuffle the dataset with labels - assert train_data.shape[0] == train_label.shape[0], ( - 'fit: data size not match for ' - f'{train_data.shape[0]} and {train_label.shape[0]}' - ) - shuffled_i = np.random.permutation(train_data.shape[0]) - shuffled_data: np.ndarray = train_data[shuffled_i] - shuffled_label: np.ndarray = train_label[shuffled_i] - - # Separate training set and development set (for validation) - train_ratio: float = 0.75 - train_size: int = floor(shuffled_data.shape[0] * train_ratio) - self.train_data = shuffled_data[:train_size] - self.train_label = shuffled_label[:train_size] - self.dev_data = shuffled_data[train_size:] - self.dev_label = shuffled_label[train_size:] - - print('=== Training ===') - - # Compare the predicted and expected results, calculate the accuracy - # for each parameter k, and find out the best k for prediction. - k_threshold: int = train_size if train_size < 20 else 20 - accuracy_table: List[float] = [0.0] - max_accuracy: float = 0.0 - - for k in range(1, k_threshold): - predicted_labels: List[int] = [] - for p in self.dev_data: - k_nearest_neighbors: List[int] = self._get_k_nearest_neighbors( - k, p, Dataset.TRAIN_SET - ) - predicted_label: int = self._get_most_common_label( - k_nearest_neighbors, Dataset.TRAIN_SET - ) - predicted_labels.append(predicted_label) - prediction: np.ndarray = np.array(predicted_labels) - - accuracy: float = np.mean(np.equal(prediction, self.dev_label)) - accuracy_table.append(accuracy) - print(f'k = {k}, train_acc = {accuracy * 100} %') - if accuracy > max_accuracy: - max_accuracy, self.k = accuracy, k - - print(f'best k = {self.k}\n') - - def predict(self, test_data: np.ndarray) -> np.ndarray: - ''' - Predict the label of a point using our model. + labels: List[int] = [all_labels[i] for i in labels_i] + return max(set(labels), key=labels.count) +``` - :param `test_data`: testing set - ''' +训练模型时,我们先将数据集打乱,然后将其中的 75% 作为训练集 `train_data`,剩下 25% 作为验证集 `dev_data`,然后使用 KNN 算法进行训练。我们选择不同的 `k` 值,通过比较验证集 `dev_data` 的预测结果和其实际标签 `dev_label`,得到每个 `k` 值所对应的预测准确率 `accuracy`。最终,我们选择准确率最高的 `k` 值作为我们将在测试集 `test_data` 上使用的参数 `k`。 - self.test_data = test_data +```python {.line-numbers} +def fit(self, train_data: np.ndarray, train_label: np.ndarray) -> None: + ''' + Train the model using a training set with labels. + :param `train_data`: training set + :param `train_label`: provided labels for data in training set + ''' + + # Shuffle the dataset with labels + assert train_data.shape[0] == train_label.shape[0], ( + 'fit: data size not match for ' + f'{train_data.shape[0]} and {train_label.shape[0]}' + ) + shuffled_i = np.random.permutation(train_data.shape[0]) + shuffled_data: np.ndarray = train_data[shuffled_i] + shuffled_label: np.ndarray = train_label[shuffled_i] + + # Separate training set and development set (for validation) + train_ratio: float = 0.75 + train_size: int = floor(shuffled_data.shape[0] * train_ratio) + self.train_data = shuffled_data[:train_size] + self.train_label = shuffled_label[:train_size] + self.dev_data = shuffled_data[train_size:] + self.dev_label = shuffled_label[train_size:] + + print('=== Training ===') + + # Compare the predicted and expected results, calculate the accuracy + # for each parameter k, and find out the best k for prediction. + k_threshold: int = train_size if train_size < 20 else 20 + accuracy_table: List[float] = [0.0] + max_accuracy: float = 0.0 + + for k in range(1, k_threshold): predicted_labels: List[int] = [] - for p in self.test_data: + for p in self.dev_data: k_nearest_neighbors: List[int] = self._get_k_nearest_neighbors( - self.k, p, Dataset.TRAIN_SET + k, p, Dataset.TRAIN_SET ) predicted_label: int = self._get_most_common_label( k_nearest_neighbors, Dataset.TRAIN_SET ) predicted_labels.append(predicted_label) prediction: np.ndarray = np.array(predicted_labels) - return prediction + + accuracy: float = np.mean(np.equal(prediction, self.dev_label)) + accuracy_table.append(accuracy) + print(f'k = {k}, train_acc = {accuracy * 100} %') + if accuracy > max_accuracy: + max_accuracy, self.k = accuracy, k + + print(f'best k = {self.k}\n') ``` -## 实验探究部分 +对测试集进行预测时,我们就使用之前得到的最优的参数 `k` 进行预测,同样使用 KNN 算法。 -生成数据集,并保存到文件: +```python {.line-numbers} +def predict(self, test_data: np.ndarray) -> np.ndarray: + ''' + Predict the label of a point using our model. + + :param `test_data`: testing set + ''' + + self.test_data = test_data + + predicted_labels: List[int] = [] + for p in self.test_data: + k_nearest_neighbors: List[int] = self._get_k_nearest_neighbors( + self.k, p, Dataset.TRAIN_SET + ) + predicted_label: int = self._get_most_common_label( + k_nearest_neighbors, Dataset.TRAIN_SET + ) + predicted_labels.append(predicted_label) + prediction: np.ndarray = np.array(predicted_labels) + return prediction +``` + +## 生成数据 + +我们使用不同的参数生成数据集,并保存到文件 `data.npy`。这里由于时间有限,为了方便起见,我们直接在函数 `generate` 的 `parameters` 变量中对相应参数进行修改。 ```python {.line-numbers} def generate() -> None: @@ -238,7 +246,7 @@ def generate() -> None: )) ``` -作图: +为了直观起见,我们提供了函数 `display` 用于将当前使用的数据集可视化,并将图片保存到 `./img` 目录下。 ```python {.line-numbers} def display(data, label, name): @@ -259,6 +267,8 @@ def display(data, label, name): ## 运行代码 +在当前目录下,我们可以使用以下参数执行代码 `source.py`,具体功能参见注释。 + ```bash # 训练模型及预测 python ./source.py g @@ -267,9 +277,11 @@ python ./source.py g python ./source.py d ``` -## 数据集 +## 实验探究 + +### 1. 实验 1 -数据生成使用的参数: +#### 1.1 参数 ```python {.line-numbers} mean = (1, 2) @@ -289,21 +301,27 @@ cov = [[10, 5], [5, 10]] size = 1000 ``` -其中,`mean` 表示数据集的均值、`cov` 表示数据集的协方差、`size` 表示数据集的大小。 +其中: -### 训练集 +- `mean` 表示数据集的均值 +- `cov` 表示数据集的协方差 +- `size` 表示数据集的大小 -![训练集](./img/train.png) +下同。 -在训练时,我们随机选取 80% 作为训练集、20% 作为验证集。 +#### 1.2 数据集 -### 测试集 +训练集: -![测试集](./img/test.png) +![训练集](./img/train_1.png) -## 参数 +测试集: -训练时使用的参数 `k` 及相应的准确率: +![测试集](./img/test_1.png) + +#### 1.3 预测准确率 + +训练时使用的参数 `k` 及相应的准确率如下所示: ```text k = 1, train_acc = 95.75 % @@ -325,8 +343,137 @@ k = 16, train_acc = 96.75 % k = 17, train_acc = 96.75 % k = 18, train_acc = 96.75 % k = 19, train_acc = 97.0 % +best k = 3 + +acc = 0.96 +``` + +可见,对于此数据集,最优的参数 `k` 为 `3`,其对测试集的预测准确率为 96%。 + +### 2. 实验 2 + +这次,我们调大数据之间的距离,观察预测准确率的变化。 + +#### 2.1 参数 + +```python {.line-numbers} +mean = (-5, 2) +cov = [[73, 0], [0, 22]] +size = 800 +``` + +```python {.line-numbers} +mean = (30, -10) +cov = [[21.2, 0], [0, 32.1]] +size = 200 +``` + +```python {.line-numbers} +mean = (20, 40) +cov = [[10, 5], [5, 10]] +size = 1000 +``` + +#### 2.2 数据集 + +训练集: + +![训练集](./img/train_2.png) + +测试集: + +![测试集](./img/test_2.png) + +#### 2.3 预测准确率 + +训练时使用的参数 `k` 及相应的准确率如下所示: + +```text +k = 1, train_acc = 100.0 % +k = 2, train_acc = 100.0 % +k = 3, train_acc = 100.0 % +k = 4, train_acc = 100.0 % +k = 5, train_acc = 100.0 % +k = 6, train_acc = 100.0 % +k = 7, train_acc = 100.0 % +k = 8, train_acc = 100.0 % +k = 9, train_acc = 100.0 % +k = 10, train_acc = 100.0 % +k = 11, train_acc = 100.0 % +k = 12, train_acc = 100.0 % +k = 13, train_acc = 100.0 % +k = 14, train_acc = 100.0 % +k = 15, train_acc = 100.0 % +k = 16, train_acc = 100.0 % +k = 17, train_acc = 100.0 % +k = 18, train_acc = 100.0 % +k = 19, train_acc = 100.0 % +best k = 1 + +acc = 1.0 +``` + +可见,对于不同标签区分度较大(即彼此间距离较远)的数据集,所有 `k` 的预测准确率均为 100%。这说明 KNN 算法对于较分散的数据有着很高的准确率。 + +### 3. 实验 3 + +#### 3.1 参数 + +```python {.line-numbers} +mean = (1, 2) +cov = [[73, 0], [0, 22]] +size = 800 ``` -可见,对于此数据集,最优的参数 `k` 为 `3`。 +```python {.line-numbers} +mean = (3, -2) +cov = [[21.2, 0], [0, 32.1]] +size = 200 +``` + +```python {.line-numbers} +mean = (-5, 4) +cov = [[10, 5], [5, 10]] +size = 1000 +``` + +#### 3.2 数据集 + +训练集: + +![训练集](./img/train_3.png) + +测试集: + +![测试集](./img/test_3.png) + +#### 3.3 预测准确率 + +训练时使用的参数 `k` 及相应的准确率如下所示: + +```text +k = 1, train_acc = 65.0 % +k = 2, train_acc = 65.75 % +k = 3, train_acc = 69.25 % +k = 4, train_acc = 68.25 % +k = 5, train_acc = 72.5 % +k = 6, train_acc = 71.5 % +k = 7, train_acc = 73.75 % +k = 8, train_acc = 75.0 % +k = 9, train_acc = 76.0 % +k = 10, train_acc = 75.75 % +k = 11, train_acc = 76.0 % +k = 12, train_acc = 74.5 % +k = 13, train_acc = 75.25 % +k = 14, train_acc = 75.0 % +k = 15, train_acc = 74.75 % +k = 16, train_acc = 75.5 % +k = 17, train_acc = 75.0 % +k = 18, train_acc = 75.0 % +k = 19, train_acc = 74.5 % +best k = 9 + +acc = 0.76 +``` -相应的准确率为 96%。 +此时,最优的参数 `k` 为 `9`,其对测试集的预测准确率为 76%。可见,当数据集间的区分度较低时,较高的 `k` 值有着相对较高的准确率。这是可以理解的,因为这样可以尽可能地减少噪声的影响。 diff --git a/assignment-1/submission/18307130003/img/test.png b/assignment-1/submission/18307130003/img/test.png deleted file mode 100644 index 77b898ff8a239b7338b029c2b7e1f76c0fd149f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25961 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 diff --git a/assignment-1/submission/18307130003/img/train.png b/assignment-1/submission/18307130003/img/train.png deleted file mode 100644 index 12c56b0d5885a7f2c0a1658dbdf18e0911783088..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38778 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 z 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 GWc>rO%;R$a diff --git a/assignment-1/submission/18307130003/tester_demo.py b/assignment-1/submission/18307130003/tester_demo.py deleted file mode 100644 index 6fe1511..0000000 --- a/assignment-1/submission/18307130003/tester_demo.py +++ /dev/null @@ -1,24 +0,0 @@ -import numpy as np - -from source import KNN - -train_data = np.array([ - [1, 2, 3, 4], - [4, 2, 3, 1], - [12, 12, 13, 14], - [14, 12, 13, 11], - [12, 14, 15, 16], -]) -train_label = np.array([0, 0, 1, 1, 1]) - -test_data = np.array([ - [3, 4, 4, 2], - [18, 14, 15, 16], -]) -test_label = np.array([0, 1]) - -model = KNN() -model.fit(train_data, train_label) -res = model.predict(test_data) - -print("acc =", np.mean(np.equal(res, test_label))) -- Gitee From f8231355368a06419640b5bd5fdc454ab1c54b08 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 2 Apr 2021 01:09:56 +0800 Subject: [PATCH 07/28] doc: minor fix --- assignment-1/submission/18307130003/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assignment-1/submission/18307130003/README.md b/assignment-1/submission/18307130003/README.md index 21cd813..511563b 100644 --- a/assignment-1/submission/18307130003/README.md +++ b/assignment-1/submission/18307130003/README.md @@ -64,7 +64,7 @@ def _get_k_nearest_neighbors( return [item[1] for item in heap] ``` -最后我们对这 k 个邻居的标签分别进行计数,选择其中出现次数最多的标签作为预测结果。 +最后我们对这 `k` 个邻居的标签分别进行计数,选择其中出现次数最多的标签作为预测结果。 ```python {.line-numbers} def _get_most_common_label( @@ -352,7 +352,7 @@ acc = 0.96 ### 2. 实验 2 -这次,我们调大数据之间的距离,观察预测准确率的变化。 +这次,我们调大数据集之间的距离,观察预测准确率的变化。 #### 2.1 参数 @@ -417,6 +417,8 @@ acc = 1.0 ### 3. 实验 3 +我们再试试减小数据集间的距离,观察预测准确率的变化。 + #### 3.1 参数 ```python {.line-numbers} -- Gitee From 5afc128d705757ddba9b996c88aac4c8185bf08c Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 2 Apr 2021 01:50:03 +0800 Subject: [PATCH 08/28] style: type annotations --- assignment-1/submission/18307130003/README.md | 81 ++++++++++++++----- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/assignment-1/submission/18307130003/README.md b/assignment-1/submission/18307130003/README.md index 511563b..2bd04be 100644 --- a/assignment-1/submission/18307130003/README.md +++ b/assignment-1/submission/18307130003/README.md @@ -156,6 +156,8 @@ def predict(self, test_data: np.ndarray) -> np.ndarray: self.test_data = test_data + print('=== Predicting ===') + predicted_labels: List[int] = [] for p in self.test_data: k_nearest_neighbors: List[int] = self._get_k_nearest_neighbors( @@ -188,7 +190,7 @@ def generate() -> None: def _generate_with_parameters(param: Parameter) -> np.ndarray: ''' - Generate a dataset using given parameters + Generate a dataset using given parameters. :param `param`: a tuple of `mean`, `cov`, `size` `mean`: the mean of the dataset @@ -249,18 +251,23 @@ def generate() -> None: 为了直观起见,我们提供了函数 `display` 用于将当前使用的数据集可视化,并将图片保存到 `./img` 目录下。 ```python {.line-numbers} -def display(data, label, name): +def display(data: np.ndarray, label: np.ndarray, name: str) -> None: ''' - Visualize data and labels using `matplotlib.pyplot`. + Visualize dataset with labels using `matplotlib.pyplot`. + + :param `data`: dataset + :param `label`: labels for data in the dataset + :param `name`: file name when saving to file ''' - datas = [[], [], []] - for i in range(len(data)): - datas[label[i]].append(data[i]) + datasets_with_label: List[List[np.ndarray]] = [[], [], []] + for i in range(data.shape[0]): + datasets_with_label[label[i]].append(data[i]) + + for dataset_with_label in datasets_with_label: + dataset_with_label_: np.ndarray = np.array(dataset_with_label) + plt.scatter(dataset_with_label_[:, 0], dataset_with_label_[:, 1]) - for each in datas: - each = np.array(each) - plt.scatter(each[:, 0], each[:, 1]) plt.savefig(f'img/{name}') plt.show() ``` @@ -277,6 +284,35 @@ python ./source.py g python ./source.py d ``` +### 输出样例 + +```text +=== Training === +k = 1, train_acc = 67.5 % +k = 2, train_acc = 67.75 % +k = 3, train_acc = 70.5 % +k = 4, train_acc = 72.5 % +k = 5, train_acc = 72.75 % +k = 6, train_acc = 73.25 % +k = 7, train_acc = 74.25 % +k = 8, train_acc = 74.0 % +k = 9, train_acc = 75.75 % +k = 10, train_acc = 75.5 % +k = 11, train_acc = 75.75 % +k = 12, train_acc = 75.5 % +k = 13, train_acc = 75.75 % +k = 14, train_acc = 75.5 % +k = 15, train_acc = 75.5 % +k = 16, train_acc = 74.5 % +k = 17, train_acc = 75.0 % +k = 18, train_acc = 75.0 % +k = 19, train_acc = 75.25 % +best k = 9 + +=== Predicting === +k = 9, predict_acc = 71.5 % +``` + ## 实验探究 ### 1. 实验 1 @@ -343,12 +379,15 @@ k = 16, train_acc = 96.75 % k = 17, train_acc = 96.75 % k = 18, train_acc = 96.75 % k = 19, train_acc = 97.0 % -best k = 3 +``` + +预测时使用的参数 `k` 及相应的准确率如下所示: -acc = 0.96 +```text +k = 3, predict_acc = 96.0 % ``` -可见,对于此数据集,最优的参数 `k` 为 `3`,其对测试集的预测准确率为 96%。 +可见,对于此数据集,最优的参数 `k` 为 `3`,其对测试集的预测准确率为 96.0 %。 ### 2. 实验 2 @@ -408,12 +447,15 @@ k = 16, train_acc = 100.0 % k = 17, train_acc = 100.0 % k = 18, train_acc = 100.0 % k = 19, train_acc = 100.0 % -best k = 1 +``` + +预测时使用的参数 `k` 及相应的准确率如下所示: -acc = 1.0 +```text +k = 1, predict_acc = 100.0 % ``` -可见,对于不同标签区分度较大(即彼此间距离较远)的数据集,所有 `k` 的预测准确率均为 100%。这说明 KNN 算法对于较分散的数据有着很高的准确率。 +可见,对于不同标签区分度较大(即彼此间距离较远)的数据集,所有 `k` 的预测准确率均为 100.0 %。这说明 KNN 算法对于较分散的数据有着很高的准确率。 ### 3. 实验 3 @@ -473,9 +515,12 @@ k = 16, train_acc = 75.5 % k = 17, train_acc = 75.0 % k = 18, train_acc = 75.0 % k = 19, train_acc = 74.5 % -best k = 9 +``` -acc = 0.76 +预测时使用的参数 `k` 及相应的准确率如下所示: + +```text +k = 9, predict_acc = 76.0 % ``` -此时,最优的参数 `k` 为 `9`,其对测试集的预测准确率为 76%。可见,当数据集间的区分度较低时,较高的 `k` 值有着相对较高的准确率。这是可以理解的,因为这样可以尽可能地减少噪声的影响。 +此时,最优的参数 `k` 为 `9`,其对测试集的预测准确率为 76.0 %。可见,当数据集间的区分度较低时,较高的 `k` 值有着相对较高的准确率。这是可以理解的,因为提高可参考的邻居数量可以尽可能地减少噪声的影响。 -- Gitee From 20cf11737ace757561b774cdfdcaaa39b4b57ca5 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Tue, 20 Apr 2021 18:08:34 +0800 Subject: [PATCH 09/28] chore: initialize --- assignment-2/submission/18307130003/README.md | 0 .../submission/18307130003/numpy_fnn.py | 153 +++++++++++++++ .../submission/18307130003/numpy_mnist.py | 36 ++++ .../submission/18307130003/tester_demo.py | 180 ++++++++++++++++++ .../submission/18307130003/torch_mnist.py | 64 +++++++ assignment-2/submission/18307130003/utils.py | 71 +++++++ 6 files changed, 504 insertions(+) create mode 100644 assignment-2/submission/18307130003/README.md create mode 100644 assignment-2/submission/18307130003/numpy_fnn.py create mode 100644 assignment-2/submission/18307130003/numpy_mnist.py create mode 100644 assignment-2/submission/18307130003/tester_demo.py create mode 100644 assignment-2/submission/18307130003/torch_mnist.py create mode 100644 assignment-2/submission/18307130003/utils.py diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md new file mode 100644 index 0000000..e69de29 diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py new file mode 100644 index 0000000..150ab17 --- /dev/null +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -0,0 +1,153 @@ +import numpy as np + + +class NumpyOp: + + def __init__(self): + self.memory = {} + self.epsilon = 1e-12 + + +class Matmul(NumpyOp): + + def forward(self, x, W): + """ + x: shape(N, d) + w: shape(d, d') + """ + self.memory['x'] = x + self.memory['W'] = W + h = np.matmul(x, W) + return h + + def backward(self, grad_y): + """ + grad_y: shape(N, d') + """ + + #################### + # code 1 # + #################### + + return grad_x, grad_W + + +class Relu(NumpyOp): + + def forward(self, x): + self.memory['x'] = x + return np.where(x > 0, x, np.zeros_like(x)) + + def backward(self, grad_y): + """ + grad_y: same shape as x + """ + + #################### + # code 2 # + #################### + + return grad_x + + +class Log(NumpyOp): + + def forward(self, x): + """ + x: shape(N, c) + """ + + out = np.log(x + self.epsilon) + self.memory['x'] = x + + return out + + def backward(self, grad_y): + """ + grad_y: same shape as x + """ + + #################### + # code 3 # + #################### + + return grad_x + + +class Softmax(NumpyOp): + """ + softmax over last dimension + """ + + def forward(self, x): + """ + x: shape(N, c) + """ + + #################### + # code 4 # + #################### + + return out + + def backward(self, grad_y): + """ + grad_y: same shape as x + """ + + #################### + # code 5 # + #################### + + return grad_x + + +class NumpyModel: + def __init__(self): + self.W1 = np.random.normal(size=(28 * 28, 256)) + self.W2 = np.random.normal(size=(256, 64)) + self.W3 = np.random.normal(size=(64, 10)) + + + # 以下算子会在 forward 和 backward 中使用 + self.matmul_1 = Matmul() + self.relu_1 = Relu() + self.matmul_2 = Matmul() + self.relu_2 = Relu() + self.matmul_3 = Matmul() + self.softmax = Softmax() + self.log = Log() + + # 以下变量需要在 backward 中更新 + self.x1_grad, self.W1_grad = None, None + self.relu_1_grad = None + self.x2_grad, self.W2_grad = None, None + self.relu_2_grad = None + self.x3_grad, self.W3_grad = None, None + self.softmax_grad = None + self.log_grad = None + + + def forward(self, x): + x = x.reshape(-1, 28 * 28) + + #################### + # code 6 # + #################### + + return x + + def backward(self, y): + for size in y.shape: + y /= size + + #################### + # code 7 # + #################### + + pass + + def optimize(self, learning_rate): + self.W1 -= learning_rate * self.W1_grad + self.W2 -= learning_rate * self.W2_grad + self.W3 -= learning_rate * self.W3_grad diff --git a/assignment-2/submission/18307130003/numpy_mnist.py b/assignment-2/submission/18307130003/numpy_mnist.py new file mode 100644 index 0000000..903d7a7 --- /dev/null +++ b/assignment-2/submission/18307130003/numpy_mnist.py @@ -0,0 +1,36 @@ +import numpy as np +from numpy_fnn import NumpyModel +from utils import download_mnist, batch, mini_batch, get_torch_initialization, plot_curve, one_hot + + +def numpy_run(): + train_dataset, test_dataset = download_mnist() + + model = NumpyModel() + model.W1, model.W2, model.W3 = get_torch_initialization() + + train_loss = [] + + epoch_number = 3 + learning_rate = 0.1 + + for epoch in range(epoch_number): + for x, y in mini_batch(train_dataset): + y = one_hot(y) + + y_pred = model.forward(x.numpy()) + loss = (-y_pred * y).sum(axis=1).mean() + model.backward(y) + model.optimize(learning_rate) + + train_loss.append(loss.item()) + + x, y = batch(test_dataset)[0] + accuracy = np.mean((model.forward(x).argmax(axis=1) == y)) + print('[{}] Accuracy: {:.4f}'.format(epoch, accuracy)) + + plot_curve(train_loss) + + +if __name__ == "__main__": + numpy_run() diff --git a/assignment-2/submission/18307130003/tester_demo.py b/assignment-2/submission/18307130003/tester_demo.py new file mode 100644 index 0000000..563de6b --- /dev/null +++ b/assignment-2/submission/18307130003/tester_demo.py @@ -0,0 +1,180 @@ +import numpy as np +import torch +from torch import matmul as torch_matmul, relu as torch_relu, softmax as torch_softmax, log as torch_log + +from numpy_fnn import Matmul, Relu, Softmax, Log, NumpyModel +from torch_mnist import TorchModel +from utils import get_torch_initialization, one_hot + +err_epsilon = 1e-3 +err_p = 0.4 + + +def check_result(numpy_result, torch_result=None): + if isinstance(numpy_result, list) and torch_result is None: + flag = True + for (n, t) in numpy_result: + flag = flag and check_result(n, t) + return flag + T = (torch_result * torch.from_numpy(numpy_result) < 0).sum().item() + direction = T / torch_result.numel() < err_p + return direction and ((torch.from_numpy(numpy_result) - torch_result).abs().mean() < err_epsilon).item() + + +def case_1(): + x = np.random.normal(size=[5, 6]) + W = np.random.normal(size=[6, 4]) + + numpy_matmul = Matmul() + numpy_out = numpy_matmul.forward(x, W) + numpy_x_grad, numpy_W_grad = numpy_matmul.backward(np.ones_like(numpy_out)) + + torch_x = torch.from_numpy(x).clone().requires_grad_() + torch_W = torch.from_numpy(W).clone().requires_grad_() + + torch_out = torch_matmul(torch_x, torch_W) + torch_out.sum().backward() + + return check_result([ + (numpy_out, torch_out), + (numpy_x_grad, torch_x.grad), + (numpy_W_grad, torch_W.grad) + ]) + + +def case_2(): + x = np.random.normal(size=[5, 6]) + + numpy_relu = Relu() + numpy_out = numpy_relu.forward(x) + numpy_x_grad = numpy_relu.backward(np.ones_like(numpy_out)) + + torch_x = torch.from_numpy(x).clone().requires_grad_() + + torch_out = torch_relu(torch_x) + torch_out.sum().backward() + + return check_result([ + (numpy_out, torch_out), + (numpy_x_grad, torch_x.grad), + ]) + + +def case_3(): + x = np.random.uniform(low=0.0, high=1.0, size=[3, 4]) + + numpy_log = Log() + numpy_out = numpy_log.forward(x) + numpy_x_grad = numpy_log.backward(np.ones_like(numpy_out)) + + torch_x = torch.from_numpy(x).clone().requires_grad_() + + torch_out = torch_log(torch_x) + torch_out.sum().backward() + + return check_result([ + (numpy_out, torch_out), + (numpy_x_grad, torch_x.grad), + ]) + + +def case_4(): + x = np.random.normal(size=[4, 5]) + + numpy_softmax = Softmax() + numpy_out = numpy_softmax.forward(x) + + torch_x = torch.from_numpy(x).clone().requires_grad_() + + torch_out = torch_softmax(torch_x, 1) + + return check_result(numpy_out, torch_out) + + +def case_5(): + x = np.random.normal(size=[20, 25]) + + numpy_softmax = Softmax() + numpy_out = numpy_softmax.forward(x) + numpy_x_grad = numpy_softmax.backward(np.ones_like(numpy_out)) + + torch_x = torch.from_numpy(x).clone().requires_grad_() + + torch_out = torch_softmax(torch_x, 1) + torch_out.sum().backward() + + return check_result([ + (numpy_out, torch_out), + (numpy_x_grad, torch_x.grad), + ]) + + +def test_model(): + try: + numpy_model = NumpyModel() + torch_model = TorchModel() + torch_model.W1.data, torch_model.W2.data, torch_model.W3.data = get_torch_initialization(numpy=False) + numpy_model.W1 = torch_model.W1.detach().clone().numpy() + numpy_model.W2 = torch_model.W2.detach().clone().numpy() + numpy_model.W3 = torch_model.W3.detach().clone().numpy() + + x = torch.randn((10000, 28, 28)) + y = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 0] * 1000) + + y = one_hot(y, numpy=False) + x2 = x.numpy() + y_pred = torch_model.forward(x) + loss = (-y_pred * y).sum(dim=1).mean() + loss.backward() + + y_pred_numpy = numpy_model.forward(x2) + y2 = y.numpy() + loss_numpy = (-y_pred_numpy * y2).sum(axis=1).mean() + + check_flag_1 = check_result(y_pred_numpy, y_pred) + print("+ {:12} {}/{}".format("forward", 10 * check_flag_1, 10)) + except: + print("[Runtime Error in forward]") + print("+ {:12} {}/{}".format("forward", 0, 10)) + return 0 + + try: + + numpy_model.backward(y2) + + check_flag_2 = [ + check_result(numpy_model.log_grad, torch_model.log.grad), + check_result(numpy_model.softmax_grad, torch_model.softmax.grad), + check_result(numpy_model.W3_grad, torch_model.W3.grad), + check_result(numpy_model.W2_grad, torch_model.W2.grad), + check_result(numpy_model.W1_grad, torch_model.W1.grad) + ] + check_flag_2 = sum(check_flag_2) >= 4 + print("+ {:12} {}/{}".format("backward", 20 * check_flag_2, 20)) + except: + print("[Runtime Error in backward]") + print("+ {:12} {}/{}".format("backward", 0, 20)) + check_flag_2 = False + + return 10 * check_flag_1 + 20 * check_flag_2 + + +if __name__ == "__main__": + testcases = [ + ["matmul", case_1, 5], + ["relu", case_2, 5], + ["log", case_3, 5], + ["softmax_1", case_4, 5], + ["softmax_2", case_5, 10], + ] + score = 0 + for case in testcases: + try: + res = case[2] if case[1]() else 0 + except: + print("[Runtime Error in {}]".format(case[0])) + res = 0 + score += res + print("+ {:12} {}/{}".format(case[0], res, case[2])) + score += test_model() + print("{:14} {}/60".format("FINAL SCORE", score)) diff --git a/assignment-2/submission/18307130003/torch_mnist.py b/assignment-2/submission/18307130003/torch_mnist.py new file mode 100644 index 0000000..6a5649b --- /dev/null +++ b/assignment-2/submission/18307130003/torch_mnist.py @@ -0,0 +1,64 @@ +import torch +from utils import mini_batch, batch, download_mnist, get_torch_initialization, one_hot, plot_curve + + +class TorchModel: + + def __init__(self): + self.W1 = torch.randn((28 * 28, 256), requires_grad=True) + self.W2 = torch.randn((256, 64), requires_grad=True) + self.W3 = torch.randn((64, 10), requires_grad=True) + + def forward(self, x): + x = x.reshape(-1, 28 * 28) + x = torch.relu(torch.matmul(x, self.W1)) + x = torch.relu(torch.matmul(x, self.W2)) + x = torch.matmul(x, self.W3) + self.softmax = torch.softmax(x, 1) + self.log = torch.log(self.softmax) + self.softmax.retain_grad() # for test only + self.log.retain_grad() # for test only + return self.log + + def optimize(self, learning_rate): + with torch.no_grad(): + self.W1 -= learning_rate * self.W1.grad + self.W2 -= learning_rate * self.W2.grad + self.W3 -= learning_rate * self.W3.grad + + self.W1.grad = None + self.W2.grad = None + self.W3.grad = None + + +def torch_run(): + train_dataset, test_dataset = download_mnist() + + model = TorchModel() + model.W1.data, model.W2.data, model.W3.data = get_torch_initialization(numpy=False) + + train_loss = [] + + epoch_number = 3 + learning_rate = 0.1 + + for epoch in range(epoch_number): + for x, y in mini_batch(train_dataset, numpy=False): + y = one_hot(y, numpy=False) + + y_pred = model.forward(x) + loss = (-y_pred * y).sum(dim=1).mean() + loss.backward() + model.optimize(learning_rate) + + train_loss.append(loss.item()) + + x, y = batch(test_dataset, numpy=False)[0] + accuracy = model.forward(x).argmax(dim=1).eq(y).float().mean().item() + print('[{}] Accuracy: {:.4f}'.format(epoch, accuracy)) + + plot_curve(train_loss) + + +if __name__ == "__main__": + torch_run() diff --git a/assignment-2/submission/18307130003/utils.py b/assignment-2/submission/18307130003/utils.py new file mode 100644 index 0000000..709220c --- /dev/null +++ b/assignment-2/submission/18307130003/utils.py @@ -0,0 +1,71 @@ +import torch +import numpy as np +from matplotlib import pyplot as plt + + +def plot_curve(data): + plt.plot(range(len(data)), data, color='blue') + plt.legend(['loss_value'], loc='upper right') + plt.xlabel('step') + plt.ylabel('value') + plt.show() + + +def download_mnist(): + from torchvision import datasets, transforms + + transform = transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize(mean=(0.1307,), std=(0.3081,)) + ]) + + train_dataset = datasets.MNIST(root="./data/", transform=transform, train=True, download=True) + test_dataset = datasets.MNIST(root="./data/", transform=transform, train=False, download=True) + + return train_dataset, test_dataset + + +def one_hot(y, numpy=True): + if numpy: + y_ = np.zeros((y.shape[0], 10)) + y_[np.arange(y.shape[0], dtype=np.int32), y] = 1 + return y_ + else: + y_ = torch.zeros((y.shape[0], 10)) + y_[torch.arange(y.shape[0], dtype=torch.long), y] = 1 + return y_ + + +def batch(dataset, numpy=True): + data = [] + label = [] + for each in dataset: + data.append(each[0]) + label.append(each[1]) + data = torch.stack(data) + label = torch.LongTensor(label) + if numpy: + return [(data.numpy(), label.numpy())] + else: + return [(data, label)] + + +def mini_batch(dataset, batch_size=128, numpy=False): + return torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True) + + +def get_torch_initialization(numpy=True): + fc1 = torch.nn.Linear(28 * 28, 256) + fc2 = torch.nn.Linear(256, 64) + fc3 = torch.nn.Linear(64, 10) + + if numpy: + W1 = fc1.weight.T.detach().clone().numpy() + W2 = fc2.weight.T.detach().clone().numpy() + W3 = fc3.weight.T.detach().clone().numpy() + else: + W1 = fc1.weight.T.detach().clone().data + W2 = fc2.weight.T.detach().clone().data + W3 = fc3.weight.T.detach().clone().data + + return W1, W2, W3 -- Gitee From 1c81868708fae8a169cc662a53c467fc1fb47595 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Tue, 20 Apr 2021 18:15:48 +0800 Subject: [PATCH 10/28] style: fix coding style --- .../submission/18307130003/numpy_fnn.py | 58 +++++++++--------- .../submission/18307130003/numpy_mnist.py | 16 ++--- .../submission/18307130003/tester_demo.py | 60 ++++++++++--------- .../submission/18307130003/torch_mnist.py | 28 +++++---- assignment-2/submission/18307130003/utils.py | 18 +++--- 5 files changed, 93 insertions(+), 87 deletions(-) diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index 150ab17..f50cb4c 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -2,14 +2,14 @@ import numpy as np class NumpyOp: - + def __init__(self): self.memory = {} self.epsilon = 1e-12 class Matmul(NumpyOp): - + def forward(self, x, W): """ x: shape(N, d) @@ -19,58 +19,58 @@ class Matmul(NumpyOp): self.memory['W'] = W h = np.matmul(x, W) return h - + def backward(self, grad_y): """ grad_y: shape(N, d') """ - + #################### # code 1 # #################### - + return grad_x, grad_W class Relu(NumpyOp): - + def forward(self, x): self.memory['x'] = x return np.where(x > 0, x, np.zeros_like(x)) - + def backward(self, grad_y): """ grad_y: same shape as x """ - + #################### # code 2 # #################### - + return grad_x class Log(NumpyOp): - + def forward(self, x): """ x: shape(N, c) """ - + out = np.log(x + self.epsilon) self.memory['x'] = x - + return out - + def backward(self, grad_y): """ grad_y: same shape as x """ - + #################### # code 3 # #################### - + return grad_x @@ -78,27 +78,27 @@ class Softmax(NumpyOp): """ softmax over last dimension """ - + def forward(self, x): """ x: shape(N, c) """ - + #################### # code 4 # #################### - + return out - + def backward(self, grad_y): """ grad_y: same shape as x """ - + #################### # code 5 # #################### - + return grad_x @@ -108,7 +108,6 @@ class NumpyModel: self.W2 = np.random.normal(size=(256, 64)) self.W3 = np.random.normal(size=(64, 10)) - # 以下算子会在 forward 和 backward 中使用 self.matmul_1 = Matmul() self.relu_1 = Relu() @@ -126,27 +125,26 @@ class NumpyModel: self.x3_grad, self.W3_grad = None, None self.softmax_grad = None self.log_grad = None - - + def forward(self, x): x = x.reshape(-1, 28 * 28) - + #################### # code 6 # #################### - + return x - + def backward(self, y): for size in y.shape: y /= size - + #################### # code 7 # #################### - + pass - + def optimize(self, learning_rate): self.W1 -= learning_rate * self.W1_grad self.W2 -= learning_rate * self.W2_grad diff --git a/assignment-2/submission/18307130003/numpy_mnist.py b/assignment-2/submission/18307130003/numpy_mnist.py index 903d7a7..c92e1a7 100644 --- a/assignment-2/submission/18307130003/numpy_mnist.py +++ b/assignment-2/submission/18307130003/numpy_mnist.py @@ -5,30 +5,30 @@ from utils import download_mnist, batch, mini_batch, get_torch_initialization, p def numpy_run(): train_dataset, test_dataset = download_mnist() - + model = NumpyModel() model.W1, model.W2, model.W3 = get_torch_initialization() - + train_loss = [] - + epoch_number = 3 learning_rate = 0.1 - + for epoch in range(epoch_number): for x, y in mini_batch(train_dataset): y = one_hot(y) - + y_pred = model.forward(x.numpy()) loss = (-y_pred * y).sum(axis=1).mean() model.backward(y) model.optimize(learning_rate) - + train_loss.append(loss.item()) - + x, y = batch(test_dataset)[0] accuracy = np.mean((model.forward(x).argmax(axis=1) == y)) print('[{}] Accuracy: {:.4f}'.format(epoch, accuracy)) - + plot_curve(train_loss) diff --git a/assignment-2/submission/18307130003/tester_demo.py b/assignment-2/submission/18307130003/tester_demo.py index 563de6b..39e137c 100644 --- a/assignment-2/submission/18307130003/tester_demo.py +++ b/assignment-2/submission/18307130003/tester_demo.py @@ -24,17 +24,17 @@ def check_result(numpy_result, torch_result=None): def case_1(): x = np.random.normal(size=[5, 6]) W = np.random.normal(size=[6, 4]) - + numpy_matmul = Matmul() numpy_out = numpy_matmul.forward(x, W) numpy_x_grad, numpy_W_grad = numpy_matmul.backward(np.ones_like(numpy_out)) - + torch_x = torch.from_numpy(x).clone().requires_grad_() torch_W = torch.from_numpy(W).clone().requires_grad_() - + torch_out = torch_matmul(torch_x, torch_W) torch_out.sum().backward() - + return check_result([ (numpy_out, torch_out), (numpy_x_grad, torch_x.grad), @@ -44,16 +44,16 @@ def case_1(): def case_2(): x = np.random.normal(size=[5, 6]) - + numpy_relu = Relu() numpy_out = numpy_relu.forward(x) numpy_x_grad = numpy_relu.backward(np.ones_like(numpy_out)) - + torch_x = torch.from_numpy(x).clone().requires_grad_() - + torch_out = torch_relu(torch_x) torch_out.sum().backward() - + return check_result([ (numpy_out, torch_out), (numpy_x_grad, torch_x.grad), @@ -62,16 +62,16 @@ def case_2(): def case_3(): x = np.random.uniform(low=0.0, high=1.0, size=[3, 4]) - + numpy_log = Log() numpy_out = numpy_log.forward(x) numpy_x_grad = numpy_log.backward(np.ones_like(numpy_out)) - + torch_x = torch.from_numpy(x).clone().requires_grad_() - + torch_out = torch_log(torch_x) torch_out.sum().backward() - + return check_result([ (numpy_out, torch_out), (numpy_x_grad, torch_x.grad), @@ -80,29 +80,29 @@ def case_3(): def case_4(): x = np.random.normal(size=[4, 5]) - + numpy_softmax = Softmax() numpy_out = numpy_softmax.forward(x) - + torch_x = torch.from_numpy(x).clone().requires_grad_() - + torch_out = torch_softmax(torch_x, 1) - + return check_result(numpy_out, torch_out) def case_5(): x = np.random.normal(size=[20, 25]) - + numpy_softmax = Softmax() numpy_out = numpy_softmax.forward(x) numpy_x_grad = numpy_softmax.backward(np.ones_like(numpy_out)) - + torch_x = torch.from_numpy(x).clone().requires_grad_() - + torch_out = torch_softmax(torch_x, 1) torch_out.sum().backward() - + return check_result([ (numpy_out, torch_out), (numpy_x_grad, torch_x.grad), @@ -113,35 +113,37 @@ def test_model(): try: numpy_model = NumpyModel() torch_model = TorchModel() - torch_model.W1.data, torch_model.W2.data, torch_model.W3.data = get_torch_initialization(numpy=False) + torch_model.W1.data, torch_model.W2.data, torch_model.W3.data = ( + get_torch_initialization(numpy=False) + ) numpy_model.W1 = torch_model.W1.detach().clone().numpy() numpy_model.W2 = torch_model.W2.detach().clone().numpy() numpy_model.W3 = torch_model.W3.detach().clone().numpy() - + x = torch.randn((10000, 28, 28)) y = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 0] * 1000) - + y = one_hot(y, numpy=False) x2 = x.numpy() y_pred = torch_model.forward(x) loss = (-y_pred * y).sum(dim=1).mean() loss.backward() - + y_pred_numpy = numpy_model.forward(x2) y2 = y.numpy() loss_numpy = (-y_pred_numpy * y2).sum(axis=1).mean() - + check_flag_1 = check_result(y_pred_numpy, y_pred) print("+ {:12} {}/{}".format("forward", 10 * check_flag_1, 10)) except: print("[Runtime Error in forward]") print("+ {:12} {}/{}".format("forward", 0, 10)) return 0 - + try: - + numpy_model.backward(y2) - + check_flag_2 = [ check_result(numpy_model.log_grad, torch_model.log.grad), check_result(numpy_model.softmax_grad, torch_model.softmax.grad), @@ -155,7 +157,7 @@ def test_model(): print("[Runtime Error in backward]") print("+ {:12} {}/{}".format("backward", 0, 20)) check_flag_2 = False - + return 10 * check_flag_1 + 20 * check_flag_2 diff --git a/assignment-2/submission/18307130003/torch_mnist.py b/assignment-2/submission/18307130003/torch_mnist.py index 6a5649b..c1315f0 100644 --- a/assignment-2/submission/18307130003/torch_mnist.py +++ b/assignment-2/submission/18307130003/torch_mnist.py @@ -3,12 +3,12 @@ from utils import mini_batch, batch, download_mnist, get_torch_initialization, o class TorchModel: - + def __init__(self): self.W1 = torch.randn((28 * 28, 256), requires_grad=True) self.W2 = torch.randn((256, 64), requires_grad=True) self.W3 = torch.randn((64, 10), requires_grad=True) - + def forward(self, x): x = x.reshape(-1, 28 * 28) x = torch.relu(torch.matmul(x, self.W1)) @@ -19,13 +19,13 @@ class TorchModel: self.softmax.retain_grad() # for test only self.log.retain_grad() # for test only return self.log - + def optimize(self, learning_rate): with torch.no_grad(): self.W1 -= learning_rate * self.W1.grad self.W2 -= learning_rate * self.W2.grad self.W3 -= learning_rate * self.W3.grad - + self.W1.grad = None self.W2.grad = None self.W3.grad = None @@ -33,30 +33,32 @@ class TorchModel: def torch_run(): train_dataset, test_dataset = download_mnist() - + model = TorchModel() - model.W1.data, model.W2.data, model.W3.data = get_torch_initialization(numpy=False) - + model.W1.data, model.W2.data, model.W3.data = ( + get_torch_initialization(numpy=False) + ) + train_loss = [] - + epoch_number = 3 learning_rate = 0.1 - + for epoch in range(epoch_number): for x, y in mini_batch(train_dataset, numpy=False): y = one_hot(y, numpy=False) - + y_pred = model.forward(x) loss = (-y_pred * y).sum(dim=1).mean() loss.backward() model.optimize(learning_rate) - + train_loss.append(loss.item()) - + x, y = batch(test_dataset, numpy=False)[0] accuracy = model.forward(x).argmax(dim=1).eq(y).float().mean().item() print('[{}] Accuracy: {:.4f}'.format(epoch, accuracy)) - + plot_curve(train_loss) diff --git a/assignment-2/submission/18307130003/utils.py b/assignment-2/submission/18307130003/utils.py index 709220c..f471049 100644 --- a/assignment-2/submission/18307130003/utils.py +++ b/assignment-2/submission/18307130003/utils.py @@ -13,15 +13,19 @@ def plot_curve(data): def download_mnist(): from torchvision import datasets, transforms - + transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean=(0.1307,), std=(0.3081,)) ]) - - train_dataset = datasets.MNIST(root="./data/", transform=transform, train=True, download=True) - test_dataset = datasets.MNIST(root="./data/", transform=transform, train=False, download=True) - + + train_dataset = datasets.MNIST( + root="./data/", transform=transform, train=True, download=True + ) + test_dataset = datasets.MNIST( + root="./data/", transform=transform, train=False, download=True + ) + return train_dataset, test_dataset @@ -58,7 +62,7 @@ def get_torch_initialization(numpy=True): fc1 = torch.nn.Linear(28 * 28, 256) fc2 = torch.nn.Linear(256, 64) fc3 = torch.nn.Linear(64, 10) - + if numpy: W1 = fc1.weight.T.detach().clone().numpy() W2 = fc2.weight.T.detach().clone().numpy() @@ -67,5 +71,5 @@ def get_torch_initialization(numpy=True): W1 = fc1.weight.T.detach().clone().data W2 = fc2.weight.T.detach().clone().data W3 = fc3.weight.T.detach().clone().data - + return W1, W2, W3 -- Gitee From 14b8a71153a5c2356a9bb51194c3f964c7b13d31 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Wed, 21 Apr 2021 03:04:08 +0800 Subject: [PATCH 11/28] feat: implement Matmul, Relu, Log style: fix coding style --- .../submission/18307130003/numpy_fnn.py | 133 +++++++++++------- .../submission/18307130003/tester_demo.py | 4 +- 2 files changed, 86 insertions(+), 51 deletions(-) diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index f50cb4c..f17c5f3 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -1,7 +1,11 @@ +from typing import Tuple import numpy as np class NumpyOp: + ''' + The base class for Numpy operations. + ''' def __init__(self): self.memory = {} @@ -9,97 +13,128 @@ class NumpyOp: class Matmul(NumpyOp): + ''' + Matrix multiplication unit.. + ''' + + def forward(self, x: np.ndarray, w: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) + w: shape(d, d') + + Returns: + shape(N, d') + ''' - def forward(self, x, W): - """ - x: shape(N, d) - w: shape(d, d') - """ self.memory['x'] = x - self.memory['W'] = W - h = np.matmul(x, W) - return h + self.memory['w'] = w + return np.matmul(x, w) - def backward(self, grad_y): - """ - grad_y: shape(N, d') - """ + def backward(self, grad_y: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: + ''' + Args: + grad_y: shape(N, d') - #################### - # code 1 # - #################### + Returns: + grad_x: shape(N, d) + grad_w: shape(d, d') + ''' - return grad_x, grad_W + x: np.ndarray = self.memory['x'] + w: np.ndarray = self.memory['w'] + grad_x: np.ndarray = np.matmul(grad_y, w.T) + grad_w: np.ndarray = np.matmul(x.T, grad_y) + return grad_x, grad_w class Relu(NumpyOp): + ''' + Rectified Linear Unit. + ''' + + def forward(self, x: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) + + Returns: + shape(N, d) + ''' - def forward(self, x): self.memory['x'] = x - return np.where(x > 0, x, np.zeros_like(x)) + return np.where(x > 0, x, 0) - def backward(self, grad_y): - """ - grad_y: same shape as x - """ + def backward(self, grad_y: np.ndarray) -> np.ndarray: + ''' + Args: + grad_y: shape(N, d) - #################### - # code 2 # - #################### + Returns: + shape(N, d) + ''' - return grad_x + x: np.ndarray = self.memory['x'] + grad_y[x < 0] = 0 + return grad_y class Log(NumpyOp): + ''' + Natural logarithm unit. + ''' - def forward(self, x): - """ - x: shape(N, c) - """ + def forward(self, x: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) - out = np.log(x + self.epsilon) - self.memory['x'] = x + Returns: + shape(N, d) + ''' - return out + self.memory['x'] = x + return np.log(x + self.epsilon) - def backward(self, grad_y): - """ - grad_y: same shape as x - """ + def backward(self, grad_y: np.ndarray) -> np.ndarray: + ''' + Args: + grad_y: shape(N, d) - #################### - # code 3 # - #################### + Returns: + shape(N, d) + ''' - return grad_x + x: np.ndarray = self.memory['x'] + return grad_y / x class Softmax(NumpyOp): - """ + ''' softmax over last dimension - """ + ''' def forward(self, x): - """ + ''' x: shape(N, c) - """ + ''' #################### # code 4 # #################### - return out + # return out def backward(self, grad_y): - """ + ''' grad_y: same shape as x - """ + ''' #################### # code 5 # #################### - return grad_x + # return grad_x class NumpyModel: diff --git a/assignment-2/submission/18307130003/tester_demo.py b/assignment-2/submission/18307130003/tester_demo.py index 39e137c..c2c9bc5 100644 --- a/assignment-2/submission/18307130003/tester_demo.py +++ b/assignment-2/submission/18307130003/tester_demo.py @@ -173,8 +173,8 @@ if __name__ == "__main__": for case in testcases: try: res = case[2] if case[1]() else 0 - except: - print("[Runtime Error in {}]".format(case[0])) + except Exception as err: + print("[Runtime Error in {}, error: {}]".format(case[0], err)) res = 0 score += res print("+ {:12} {}/{}".format(case[0], res, case[2])) -- Gitee From a81128c79c2c566152b4816efd4267aa59ac2e75 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 29 Apr 2021 19:06:08 +0800 Subject: [PATCH 12/28] feat: implement Softmax, NumpyModel --- .../submission/18307130003/numpy_fnn.py | 111 ++++++++++++------ 1 file changed, 72 insertions(+), 39 deletions(-) diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index f17c5f3..dbb1d34 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -111,76 +111,109 @@ class Log(NumpyOp): class Softmax(NumpyOp): ''' - softmax over last dimension + Softmax over last dimension. ''' - def forward(self, x): - ''' - x: shape(N, c) + def forward(self, x: np.ndarray) -> np.ndarray: ''' + Args: + x: shape(N, d) - #################### - # code 4 # - #################### + Returns: + shape(N, d) + ''' - # return out + y: np.ndarray = np.exp(x) / np.exp(x).sum(axis=1)[:, None] + self.memory['y'] = y + return y - def backward(self, grad_y): - ''' - grad_y: same shape as x + def backward(self, grad_y: np.ndarray) -> np.ndarray: ''' + Args: + grad_y: shape(N, d) - #################### - # code 5 # - #################### + Returns: + shape(N, d) + ''' - # return grad_x + y: np.ndarray = self.memory['y'] + return y * (grad_y - (grad_y * y).sum(axis=1)[:, None]) class NumpyModel: + ''' + An FNN model implemented in NumPy. + ''' + def __init__(self): - self.W1 = np.random.normal(size=(28 * 28, 256)) - self.W2 = np.random.normal(size=(256, 64)) - self.W3 = np.random.normal(size=(64, 10)) + self.W1: np.ndarray = np.random.normal(size=(28 * 28, 256)) + self.W2: np.ndarray = np.random.normal(size=(256, 64)) + self.W3: np.ndarray = np.random.normal(size=(64, 10)) # 以下算子会在 forward 和 backward 中使用 + self.matmul_1 = Matmul() self.relu_1 = Relu() + self.matmul_2 = Matmul() self.relu_2 = Relu() + self.matmul_3 = Matmul() - self.softmax = Softmax() - self.log = Log() + self.softmax_3 = Softmax() + self.log_3 = Log() # 以下变量需要在 backward 中更新 - self.x1_grad, self.W1_grad = None, None - self.relu_1_grad = None - self.x2_grad, self.W2_grad = None, None - self.relu_2_grad = None - self.x3_grad, self.W3_grad = None, None - self.softmax_grad = None - self.log_grad = None - - def forward(self, x): - x = x.reshape(-1, 28 * 28) - #################### - # code 6 # - #################### + self.x1_grad: np.ndarray = None + self.W1_grad: np.ndarray = None + self.relu_1_grad: np.ndarray = None + + self.x2_grad: np.ndarray = None + self.W2_grad: np.ndarray = None + self.relu_2_grad: np.ndarray = None + + self.x3_grad: np.ndarray = None + self.W3_grad: np.ndarray = None + self.softmax_grad: np.ndarray = None + self.log_grad: np.ndarray = None + + def forward(self, x: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) + Returns: + shape(N, d) + ''' + + x = x.reshape(-1, 28 * 28) + x = self.relu_1.forward(self.matmul_1.forward(x, self.W1)) + x = self.relu_2.forward(self.matmul_2.forward(x, self.W2)) + x = self.softmax_3.forward(self.matmul_3.forward(x, self.W3)) + x = self.log_3.forward(x) return x - def backward(self, y): + def backward(self, y: np.ndarray) -> None: + ''' + Args: + y: shape(N, d) + ''' + for size in y.shape: + size: int y /= size - #################### - # code 7 # - #################### + self.log_grad = self.log_3.backward(y) + self.softmax_grad = self.softmax_3.backward(self.log_grad) + self.x3_grad, self.W3_grad = self.matmul_3.backward(self.softmax_grad) + + self.relu_2_grad = self.relu_2.backward(self.x3_grad) + self.x2_grad, self.W2_grad = self.matmul_2.backward(self.relu_2_grad) - pass + self.relu_1_grad = self.relu_1.backward(self.x2_grad) + self.x1_grad, self.W1_grad = self.matmul_1.backward(self.relu_1_grad) - def optimize(self, learning_rate): + def optimize(self, learning_rate: float): self.W1 -= learning_rate * self.W1_grad self.W2 -= learning_rate * self.W2_grad self.W3 -= learning_rate * self.W3_grad -- Gitee From 6e1151dfcc6eab665b5a8504ca190e5e6762df17 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 29 Apr 2021 19:06:26 +0800 Subject: [PATCH 13/28] feat: add more logs in tester --- .../submission/18307130003/tester_demo.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/assignment-2/submission/18307130003/tester_demo.py b/assignment-2/submission/18307130003/tester_demo.py index c2c9bc5..c607208 100644 --- a/assignment-2/submission/18307130003/tester_demo.py +++ b/assignment-2/submission/18307130003/tester_demo.py @@ -18,7 +18,9 @@ def check_result(numpy_result, torch_result=None): return flag T = (torch_result * torch.from_numpy(numpy_result) < 0).sum().item() direction = T / torch_result.numel() < err_p - return direction and ((torch.from_numpy(numpy_result) - torch_result).abs().mean() < err_epsilon).item() + result = direction and ((torch.from_numpy(numpy_result) - torch_result).abs().mean() < err_epsilon).item() + print(result) + return result def case_1(): @@ -135,13 +137,12 @@ def test_model(): check_flag_1 = check_result(y_pred_numpy, y_pred) print("+ {:12} {}/{}".format("forward", 10 * check_flag_1, 10)) - except: - print("[Runtime Error in forward]") + except Exception as err: + print("[Runtime Error in forward, error: {}]".format(err)) print("+ {:12} {}/{}".format("forward", 0, 10)) return 0 try: - numpy_model.backward(y2) check_flag_2 = [ @@ -151,10 +152,10 @@ def test_model(): check_result(numpy_model.W2_grad, torch_model.W2.grad), check_result(numpy_model.W1_grad, torch_model.W1.grad) ] - check_flag_2 = sum(check_flag_2) >= 4 + check_flag_2 = sum(check_flag_2) / 5 print("+ {:12} {}/{}".format("backward", 20 * check_flag_2, 20)) - except: - print("[Runtime Error in backward]") + except Exception as err: + print("[Runtime Error in backward, error: {}]".format(err)) print("+ {:12} {}/{}".format("backward", 0, 20)) check_flag_2 = False -- Gitee From 56125d6d12009eeb7c148c97d81a995a9396c291 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 29 Apr 2021 19:06:29 +0800 Subject: [PATCH 14/28] fix: negative loss function issue --- .../submission/18307130003/numpy_fnn.py | 19 +------------------ .../submission/18307130003/tester_demo.py | 8 +++++--- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index dbb1d34..ac9ee2f 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -150,8 +150,6 @@ class NumpyModel: self.W2: np.ndarray = np.random.normal(size=(256, 64)) self.W3: np.ndarray = np.random.normal(size=(64, 10)) - # 以下算子会在 forward 和 backward 中使用 - self.matmul_1 = Matmul() self.relu_1 = Relu() @@ -162,21 +160,6 @@ class NumpyModel: self.softmax_3 = Softmax() self.log_3 = Log() - # 以下变量需要在 backward 中更新 - - self.x1_grad: np.ndarray = None - self.W1_grad: np.ndarray = None - self.relu_1_grad: np.ndarray = None - - self.x2_grad: np.ndarray = None - self.W2_grad: np.ndarray = None - self.relu_2_grad: np.ndarray = None - - self.x3_grad: np.ndarray = None - self.W3_grad: np.ndarray = None - self.softmax_grad: np.ndarray = None - self.log_grad: np.ndarray = None - def forward(self, x: np.ndarray) -> np.ndarray: ''' Args: @@ -203,7 +186,7 @@ class NumpyModel: size: int y /= size - self.log_grad = self.log_3.backward(y) + self.log_grad = self.log_3.backward(-y) self.softmax_grad = self.softmax_3.backward(self.log_grad) self.x3_grad, self.W3_grad = self.matmul_3.backward(self.softmax_grad) diff --git a/assignment-2/submission/18307130003/tester_demo.py b/assignment-2/submission/18307130003/tester_demo.py index c607208..38530cd 100644 --- a/assignment-2/submission/18307130003/tester_demo.py +++ b/assignment-2/submission/18307130003/tester_demo.py @@ -18,7 +18,9 @@ def check_result(numpy_result, torch_result=None): return flag T = (torch_result * torch.from_numpy(numpy_result) < 0).sum().item() direction = T / torch_result.numel() < err_p - result = direction and ((torch.from_numpy(numpy_result) - torch_result).abs().mean() < err_epsilon).item() + result = direction and ( + (torch.from_numpy(numpy_result) - torch_result).abs().mean() < err_epsilon + ).item() print(result) return result @@ -153,13 +155,13 @@ def test_model(): check_result(numpy_model.W1_grad, torch_model.W1.grad) ] check_flag_2 = sum(check_flag_2) / 5 - print("+ {:12} {}/{}".format("backward", 20 * check_flag_2, 20)) + print("+ {:12} {}/{}".format("backward", int(20 * check_flag_2), 20)) except Exception as err: print("[Runtime Error in backward, error: {}]".format(err)) print("+ {:12} {}/{}".format("backward", 0, 20)) check_flag_2 = False - return 10 * check_flag_1 + 20 * check_flag_2 + return int(10 * check_flag_1 + 20 * check_flag_2) if __name__ == "__main__": -- Gitee From 9f35413968e5c9df62185e18040384187778cd8f Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 29 Apr 2021 19:06:33 +0800 Subject: [PATCH 15/28] fix: merge fixes from upstream --- .../submission/18307130003/numpy_fnn.py | 16 ++++++++++++ .../submission/18307130003/numpy_mnist.py | 24 +++++++++++++---- .../submission/18307130003/tester_demo.py | 26 ++++++++++++------- .../submission/18307130003/torch_mnist.py | 24 +++++++++++------ 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index ac9ee2f..46823b2 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -140,6 +140,22 @@ class Softmax(NumpyOp): return y * (grad_y - (grad_y * y).sum(axis=1)[:, None]) +class NumpyLoss: + ''' + Loss function. + ''' + + def __init__(self): + self.target: np.ndarray = None + + def get_loss(self, pred: np.ndarray, target: np.ndarray) -> float: + self.target = target + return (-pred * target).sum(axis=1).mean() + + def backward(self) -> np.ndarray: + return -self.target / self.target.shape[0] + + class NumpyModel: ''' An FNN model implemented in NumPy. diff --git a/assignment-2/submission/18307130003/numpy_mnist.py b/assignment-2/submission/18307130003/numpy_mnist.py index c92e1a7..630e38f 100644 --- a/assignment-2/submission/18307130003/numpy_mnist.py +++ b/assignment-2/submission/18307130003/numpy_mnist.py @@ -1,15 +1,28 @@ +from typing import List import numpy as np -from numpy_fnn import NumpyModel -from utils import download_mnist, batch, mini_batch, get_torch_initialization, plot_curve, one_hot +from numpy_fnn import NumpyModel, NumpyLoss +from utils import ( + download_mnist, + batch, + mini_batch, + get_torch_initialization, + plot_curve, + one_hot, +) def numpy_run(): + ''' + Train the FNN network on MNIST. + ''' + train_dataset, test_dataset = download_mnist() model = NumpyModel() + numpy_loss = NumpyLoss() model.W1, model.W2, model.W3 = get_torch_initialization() - train_loss = [] + train_loss: List[float] = [] epoch_number = 3 learning_rate = 0.1 @@ -19,8 +32,9 @@ def numpy_run(): y = one_hot(y) y_pred = model.forward(x.numpy()) - loss = (-y_pred * y).sum(axis=1).mean() - model.backward(y) + loss = numpy_loss.get_loss(y_pred, y) + + model.backward(numpy_loss.backward()) model.optimize(learning_rate) train_loss.append(loss.item()) diff --git a/assignment-2/submission/18307130003/tester_demo.py b/assignment-2/submission/18307130003/tester_demo.py index 38530cd..68874de 100644 --- a/assignment-2/submission/18307130003/tester_demo.py +++ b/assignment-2/submission/18307130003/tester_demo.py @@ -1,12 +1,17 @@ import numpy as np import torch -from torch import matmul as torch_matmul, relu as torch_relu, softmax as torch_softmax, log as torch_log - -from numpy_fnn import Matmul, Relu, Softmax, Log, NumpyModel +from torch import ( + matmul as torch_matmul, + relu as torch_relu, + softmax as torch_softmax, + log as torch_log, +) + +from numpy_fnn import Matmul, Relu, Softmax, Log, NumpyModel, NumpyLoss from torch_mnist import TorchModel from utils import get_torch_initialization, one_hot -err_epsilon = 1e-3 +err_epsilon = 1e-6 err_p = 0.4 @@ -42,7 +47,7 @@ def case_1(): return check_result([ (numpy_out, torch_out), (numpy_x_grad, torch_x.grad), - (numpy_W_grad, torch_W.grad) + (numpy_W_grad, torch_W.grad), ]) @@ -115,6 +120,7 @@ def case_5(): def test_model(): try: + numpy_loss = NumpyLoss() numpy_model = NumpyModel() torch_model = TorchModel() torch_model.W1.data, torch_model.W2.data, torch_model.W3.data = ( @@ -134,8 +140,7 @@ def test_model(): loss.backward() y_pred_numpy = numpy_model.forward(x2) - y2 = y.numpy() - loss_numpy = (-y_pred_numpy * y2).sum(axis=1).mean() + numpy_loss.get_loss(y_pred_numpy, y.numpy()) check_flag_1 = check_result(y_pred_numpy, y_pred) print("+ {:12} {}/{}".format("forward", 10 * check_flag_1, 10)) @@ -145,11 +150,12 @@ def test_model(): return 0 try: - numpy_model.backward(y2) + numpy_model.backward(numpy_loss.backward()) check_flag_2 = [ - check_result(numpy_model.log_grad, torch_model.log.grad), - check_result(numpy_model.softmax_grad, torch_model.softmax.grad), + check_result(numpy_model.log_grad, torch_model.log_input.grad), + check_result(numpy_model.softmax_grad, + torch_model.softmax_input.grad), check_result(numpy_model.W3_grad, torch_model.W3.grad), check_result(numpy_model.W2_grad, torch_model.W2.grad), check_result(numpy_model.W1_grad, torch_model.W1.grad) diff --git a/assignment-2/submission/18307130003/torch_mnist.py b/assignment-2/submission/18307130003/torch_mnist.py index c1315f0..dec6c89 100644 --- a/assignment-2/submission/18307130003/torch_mnist.py +++ b/assignment-2/submission/18307130003/torch_mnist.py @@ -8,17 +8,26 @@ class TorchModel: self.W1 = torch.randn((28 * 28, 256), requires_grad=True) self.W2 = torch.randn((256, 64), requires_grad=True) self.W3 = torch.randn((64, 10), requires_grad=True) + self.softmax_input = None + self.log_input = None def forward(self, x): x = x.reshape(-1, 28 * 28) x = torch.relu(torch.matmul(x, self.W1)) x = torch.relu(torch.matmul(x, self.W2)) x = torch.matmul(x, self.W3) - self.softmax = torch.softmax(x, 1) - self.log = torch.log(self.softmax) - self.softmax.retain_grad() # for test only - self.log.retain_grad() # for test only - return self.log + + self.softmax_input = x + self.softmax_input.retain_grad() + + x = torch.softmax(x, 1) + + self.log_input = x + self.log_input.retain_grad() + + x = torch.log(x) + + return x def optimize(self, learning_rate): with torch.no_grad(): @@ -35,9 +44,8 @@ def torch_run(): train_dataset, test_dataset = download_mnist() model = TorchModel() - model.W1.data, model.W2.data, model.W3.data = ( - get_torch_initialization(numpy=False) - ) + model.W1.data, model.W2.data, model.W3.data = get_torch_initialization( + numpy=False) train_loss = [] -- Gitee From db8aba37b92361a5d49923475e04b4057a8f55a0 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 29 Apr 2021 19:06:36 +0800 Subject: [PATCH 16/28] fix: minor fix for previous changes --- assignment-2/submission/18307130003/numpy_fnn.py | 6 +----- assignment-2/submission/18307130003/numpy_mnist.py | 8 +++++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index 46823b2..6db4b6c 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -198,11 +198,7 @@ class NumpyModel: y: shape(N, d) ''' - for size in y.shape: - size: int - y /= size - - self.log_grad = self.log_3.backward(-y) + self.log_grad = self.log_3.backward(y) self.softmax_grad = self.softmax_3.backward(self.log_grad) self.x3_grad, self.W3_grad = self.matmul_3.backward(self.softmax_grad) diff --git a/assignment-2/submission/18307130003/numpy_mnist.py b/assignment-2/submission/18307130003/numpy_mnist.py index 630e38f..c584c4c 100644 --- a/assignment-2/submission/18307130003/numpy_mnist.py +++ b/assignment-2/submission/18307130003/numpy_mnist.py @@ -1,5 +1,6 @@ from typing import List import numpy as np +from torch import Tensor from numpy_fnn import NumpyModel, NumpyLoss from utils import ( download_mnist, @@ -29,9 +30,10 @@ def numpy_run(): for epoch in range(epoch_number): for x, y in mini_batch(train_dataset): - y = one_hot(y) + x: np.ndarray = x.numpy() + y: np.ndarray = one_hot(y) - y_pred = model.forward(x.numpy()) + y_pred = model.forward(x) loss = numpy_loss.get_loss(y_pred, y) model.backward(numpy_loss.backward()) @@ -40,7 +42,7 @@ def numpy_run(): train_loss.append(loss.item()) x, y = batch(test_dataset)[0] - accuracy = np.mean((model.forward(x).argmax(axis=1) == y)) + accuracy: float = np.mean((model.forward(x).argmax(axis=1) == y)) print('[{}] Accuracy: {:.4f}'.format(epoch, accuracy)) plot_curve(train_loss) -- Gitee From acd1603d3d30a6b32f4a9730a8eed6772665bf2c Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 29 Apr 2021 22:47:24 +0800 Subject: [PATCH 17/28] feat: mini_batch in numpy --- .../submission/18307130003/numpy_mnist.py | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/assignment-2/submission/18307130003/numpy_mnist.py b/assignment-2/submission/18307130003/numpy_mnist.py index c584c4c..101aac0 100644 --- a/assignment-2/submission/18307130003/numpy_mnist.py +++ b/assignment-2/submission/18307130003/numpy_mnist.py @@ -1,17 +1,43 @@ -from typing import List +from typing import Any, List, Tuple import numpy as np -from torch import Tensor from numpy_fnn import NumpyModel, NumpyLoss from utils import ( download_mnist, batch, - mini_batch, + # mini_batch, get_torch_initialization, plot_curve, one_hot, ) +def mini_batch(dataset: List[Tuple[Any, int]], batch_size=128) -> np.ndarray: + ''' + Split the data and labels from the given dataset into batches. + + Args: + dataset: the given dataset + batch_size: the size of retrieved data + + Returns: + Batches of [data, labels] pair. + ''' + + data: np.ndarray = np.array([np.array(pair[0]) for pair in dataset]) + labels: np.ndarray = np.array([pair[1] for pair in dataset]) + + # Shuffle the dataset + size: int = len(dataset) + indices: np.ndarray = np.arange(size) + np.random.shuffle(indices) + + batches: List[Tuple[np.ndarray, np.ndarray]] = [] + for i in range(0, size, batch_size): + chunk: np.ndarray = indices[i:i+batch_size] + batches.append((data[chunk], labels[chunk])) + return batches + + def numpy_run(): ''' Train the FNN network on MNIST. @@ -30,7 +56,7 @@ def numpy_run(): for epoch in range(epoch_number): for x, y in mini_batch(train_dataset): - x: np.ndarray = x.numpy() + x: np.ndarray y: np.ndarray = one_hot(y) y_pred = model.forward(x) -- Gitee From d3a0a5a592394ae2a2ef77b2e74d5233964db6f7 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Thu, 29 Apr 2021 23:02:30 +0800 Subject: [PATCH 18/28] chore: doc string minor fix --- assignment-2/submission/18307130003/numpy_fnn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index 6db4b6c..7849408 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -14,7 +14,7 @@ class NumpyOp: class Matmul(NumpyOp): ''' - Matrix multiplication unit.. + Matrix multiplication unit. ''' def forward(self, x: np.ndarray, w: np.ndarray) -> np.ndarray: -- Gitee From ebe438c05319ce7db8b323054a60c02c37fb7feb Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 30 Apr 2021 03:25:44 +0800 Subject: [PATCH 19/28] doc: backward chap. 1 --- assignment-2/submission/18307130003/README.md | 232 ++++++++++++++++++ 1 file changed, 232 insertions(+) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index e69de29..062594f 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -0,0 +1,232 @@ +# 实验报告 + +本次作业完成了选题 1 的实验内容,利用 NumPy 实现了一个 FNN 模型,并在 MNIST 数据集上进行了训练。 + +## 目录 + +[TOC] + +## FNN 算子的反向传播 + +### 1. Matmul + +#### 1.1 公式推导 + +输入一个 $n\times d$ 的矩阵 $X$ 和一个 $d\times d'$ 的矩阵 $W$,算子 `Matmul` 的正向传播公式为: + +$$ +Y = X\times W \tag{1.1} +$$ + +输出一个 $n\times d'$ 的矩阵 $Y$。 + +对于梯度的反向传播,有 + +$$ +\nabla{z} = ( + \frac{\partial z}{\partial X}_{n\times d}, + \frac{\partial z}{\partial W}_{d\times d'} +) \tag{1.2} +$$ + +我们利用向量化(vectorization)进行对矩阵求导的求解: + +$$ +\begin{split} +\mathit{vec}(\frac{\partial z}{\partial X}_{n\times d}) +&= {(\frac{\partial Y}{\partial X})^T}_{nd\times nd'} + \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ +&= {({\frac + {\partial ({\mathit{vec}(X\times W)}_{nd'})} + {\partial ({\mathit{vec}(X)}_{nd})} + })^T}_{nd\times nd'} + \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ +&= {({\frac + {\partial ( + {\mathit{vec}(X)}_{nd} + \times {(I_n\otimes W)}_{nd\times nd'} + )} + {\partial ({\mathit{vec}(X)}_{nd})} + })^T}_{nd\times nd'} + \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ +&= {(I_n\otimes W)}_{nd\times nd'} + \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ +&= \mathit{vec}({(\frac{\partial z}{\partial Y}\times W^T)}_{n\times d}) +\end{split} \tag{1.3} +$$ + +这里 $\otimes$ 表示 Kronecker 积,下标表示矩阵或向量的维度。 + +因此有 + +$$ +\frac{\partial z}{\partial X}_{n\times d} += {(\frac{\partial z}{\partial Y}\times W^T)}_{n\times d} \tag{1.4} +$$ + +类似 $(1.3)$ 的推导,同理可得 + +$$ +\frac{\partial z}{\partial W}_{d\times d'} += {(X^T\times \frac{\partial z}{\partial Y})}_{d\times d'} \tag{1.5} +$$ + +#### 1.2 代码实现 + +```python {.line-numbers} +# numpy_fnn.py + +class Matmul(NumpyOp): + ''' + Matrix multiplication unit. + ''' + + def forward(self, x: np.ndarray, w: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) + w: shape(d, d') + + Returns: + shape(N, d') + ''' + + self.memory['x'] = x + self.memory['w'] = w + return np.matmul(x, w) + + def backward(self, grad_y: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: + ''' + Args: + grad_y: shape(N, d') + + Returns: + grad_x: shape(N, d) + grad_w: shape(d, d') + ''' + + x: np.ndarray = self.memory['x'] + w: np.ndarray = self.memory['w'] + grad_x: np.ndarray = np.matmul(grad_y, w.T) + grad_w: np.ndarray = np.matmul(x.T, grad_y) + return grad_x, grad_w +``` + +### 2. Relu + +#### 2.1 公式推导 + +#### 2.2 代码实现 + +```python {.line-numbers} +# numpy_fnn.py + +class Relu(NumpyOp): + ''' + Rectified Linear Unit. + ''' + + def forward(self, x: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) + + Returns: + shape(N, d) + ''' + + self.memory['x'] = x + return np.where(x > 0, x, 0) + + def backward(self, grad_y: np.ndarray) -> np.ndarray: + ''' + Args: + grad_y: shape(N, d) + + Returns: + shape(N, d) + ''' + + x: np.ndarray = self.memory['x'] + grad_y[x < 0] = 0 + return grad_y +``` + +### 3. Log + +#### 3.1 公式推导 + +#### 3.2 代码实现 + +```python {.line-numbers} +# numpy_fnn.py + +class Log(NumpyOp): + ''' + Natural logarithm unit. + ''' + + def forward(self, x: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) + + Returns: + shape(N, d) + ''' + + self.memory['x'] = x + return np.log(x + self.epsilon) + + def backward(self, grad_y: np.ndarray) -> np.ndarray: + ''' + Args: + grad_y: shape(N, d) + + Returns: + shape(N, d) + ''' + + x: np.ndarray = self.memory['x'] + return grad_y / x +``` + +### 4. Softmax + +#### 4.1 公式推导 + +#### 4.2 代码实现 + +```python {.line-numbers} +# numpy_fnn.py + +class Softmax(NumpyOp): + ''' + Softmax over last dimension. + ''' + + def forward(self, x: np.ndarray) -> np.ndarray: + ''' + Args: + x: shape(N, d) + + Returns: + shape(N, d) + ''' + + y: np.ndarray = np.exp(x) / np.exp(x).sum(axis=1)[:, None] + self.memory['y'] = y + return y + + def backward(self, grad_y: np.ndarray) -> np.ndarray: + ''' + Args: + grad_y: shape(N, d) + + Returns: + shape(N, d) + ''' + + y: np.ndarray = self.memory['y'] + return y * (grad_y - (grad_y * y).sum(axis=1)[:, None]) +``` -- Gitee From 4bbdf057724d0e28e0e98d4ee28cb49048301f2c Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 30 Apr 2021 03:28:08 +0800 Subject: [PATCH 20/28] doc: create TOC --- assignment-2/submission/18307130003/README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index 062594f..a9ac9b0 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -4,7 +4,21 @@ ## 目录 -[TOC] +- [实验报告](#实验报告) + - [目录](#目录) + - [FNN 算子的反向传播](#fnn-算子的反向传播) + - [1. Matmul](#1-matmul) + - [1.1 公式推导](#11-公式推导) + - [1.2 代码实现](#12-代码实现) + - [2. Relu](#2-relu) + - [2.1 公式推导](#21-公式推导) + - [2.2 代码实现](#22-代码实现) + - [3. Log](#3-log) + - [3.1 公式推导](#31-公式推导) + - [3.2 代码实现](#32-代码实现) + - [4. Softmax](#4-softmax) + - [4.1 公式推导](#41-公式推导) + - [4.2 代码实现](#42-代码实现) ## FNN 算子的反向传播 -- Gitee From 10fc1f13c57febd213f7fe3cbd6cd74c38e4dba7 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 30 Apr 2021 03:42:00 +0800 Subject: [PATCH 21/28] doc: minor fix latex layout --- assignment-2/submission/18307130003/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index a9ac9b0..8216661 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -48,22 +48,22 @@ $$ $$ \begin{split} \mathit{vec}(\frac{\partial z}{\partial X}_{n\times d}) -&= {(\frac{\partial Y}{\partial X})^T}_{nd\times nd'} +&= {(\frac{\partial Y}{\partial X})^T}_{nd\times {nd'}} \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ &= {({\frac {\partial ({\mathit{vec}(X\times W)}_{nd'})} {\partial ({\mathit{vec}(X)}_{nd})} - })^T}_{nd\times nd'} + })^T}_{nd\times {nd'}} \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ &= {({\frac {\partial ( {\mathit{vec}(X)}_{nd} - \times {(I_n\otimes W)}_{nd\times nd'} + \times {(I_n\otimes W)}_{nd\times {nd'}} )} {\partial ({\mathit{vec}(X)}_{nd})} - })^T}_{nd\times nd'} + })^T}_{nd\times {nd'}} \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ -&= {(I_n\otimes W)}_{nd\times nd'} +&= {(I_n\otimes W)}_{nd\times {nd'}} \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ &= \mathit{vec}({(\frac{\partial z}{\partial Y}\times W^T)}_{n\times d}) \end{split} \tag{1.3} -- Gitee From 7e6c72802d2b88e64d9ba1ce62112bad0a3bd5b0 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Fri, 30 Apr 2021 03:58:22 +0800 Subject: [PATCH 22/28] doc: add some details for chap. 1 --- assignment-2/submission/18307130003/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index 8216661..488bde9 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -69,7 +69,18 @@ $$ \end{split} \tag{1.3} $$ -这里 $\otimes$ 表示 Kronecker 积,下标表示矩阵或向量的维度。 +这里 $\mathit{vec}(X_{m\times n})$ 表示向量 + +$$ +\begin{split} +[&x_{11}, x_{12},\ &..., x_{1n}, \\ + &x_{21}, x_{22},\ &..., x_{2n}, \\ + &&... \\ + &x_{m1}, x_{m2},\ &..., x_{mn}] +\end{split} +$$ + +$\otimes$ 表示 Kronecker 积,下标表示矩阵或向量的维度。 因此有 -- Gitee From f0d3bb584f01326bdb0a11e43641e3e041ded07d Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Sun, 2 May 2021 14:13:49 +0800 Subject: [PATCH 23/28] doc: backward chap. 2 --- assignment-2/submission/18307130003/README.md | 60 +++++++++++++++++-- .../submission/18307130003/numpy_fnn.py | 3 +- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index 488bde9..cbb037b 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -51,8 +51,8 @@ $$ &= {(\frac{\partial Y}{\partial X})^T}_{nd\times {nd'}} \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ &= {({\frac - {\partial ({\mathit{vec}(X\times W)}_{nd'})} - {\partial ({\mathit{vec}(X)}_{nd})} + {\partial {\mathit{vec}(X\times W)}_{nd'}} + {\partial {\mathit{vec}(X)}_{nd}} })^T}_{nd\times {nd'}} \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ &= {({\frac @@ -60,7 +60,7 @@ $$ {\mathit{vec}(X)}_{nd} \times {(I_n\otimes W)}_{nd\times {nd'}} )} - {\partial ({\mathit{vec}(X)}_{nd})} + {\partial {\mathit{vec}(X)}_{nd}} })^T}_{nd\times {nd'}} \times {\mathit{vec}(\frac{\partial z}{\partial Y})}_{nd'} \\ &= {(I_n\otimes W)}_{nd\times {nd'}} @@ -141,6 +141,57 @@ class Matmul(NumpyOp): #### 2.1 公式推导 +输入一个 $n\times d$ 的矩阵 $X$,对于 $X$ 中的每个元素 $X_{ij}$,算子 `Relu` 的正向传播公式为: + +$$ +Y_{ij} = \left\{ + \begin{aligned} + &X_{ij}\quad &(X_{ij} > 0) \\ + &0\quad &(X_{ij} \le 0) + \end{aligned} +\right. +\tag{2.1} +$$ + +输出一个 $n\times d$ 的矩阵 $Y$。 + +对于梯度的反向传播,有 + +$$ +\begin{split} +\nabla{z} +&= \frac{\partial z}{\partial X}_{n\times d} \\ +&= \frac{\partial z}{\partial Y}_{n\times d} + \odot \frac{\partial Y}{\partial X}_{n\times d} +\end{split} \tag{2.2} +$$ + +这里 $\odot$ 表示 Hadamard 积,即逐元素(element-wise)乘积。 + +其中,对于 $\frac{\partial Y}{\partial X}_{n\times d}$ 中的每个元素 ${Y'}_{ij}$,由 $(2.1)$ 有 + +$$ +{Y'}_{ij} = \left\{ + \begin{aligned} + &1\quad &(X_{ij} > 0) \\ + &0\quad &(X_{ij} \le 0) + \end{aligned} +\right. +\tag{2.3} +$$ + +因此,对于 $\frac{\partial z}{\partial X}_{n\times d}$ 中的每个元素 ${Z'}_{ij}$,令 ${Z_Y}' = \frac{\partial z}{\partial Y}_{n\times d}$,由 $(2.2)$ 有 + +$$ +{Z'}_{ij} = \left\{ + \begin{aligned} + &{({Z_Y}')}_{ij}\quad &(X_{ij} > 0) \\ + &0\quad &(X_{ij} \le 0) + \end{aligned} +\right. +\tag{2.4} +$$ + #### 2.2 代码实现 ```python {.line-numbers} @@ -173,8 +224,7 @@ class Relu(NumpyOp): ''' x: np.ndarray = self.memory['x'] - grad_y[x < 0] = 0 - return grad_y + return np.where(x > 0, grad_y, 0) ``` ### 3. Log diff --git a/assignment-2/submission/18307130003/numpy_fnn.py b/assignment-2/submission/18307130003/numpy_fnn.py index 7849408..4331007 100644 --- a/assignment-2/submission/18307130003/numpy_fnn.py +++ b/assignment-2/submission/18307130003/numpy_fnn.py @@ -75,8 +75,7 @@ class Relu(NumpyOp): ''' x: np.ndarray = self.memory['x'] - grad_y[x < 0] = 0 - return grad_y + return np.where(x > 0, grad_y, 0) class Log(NumpyOp): -- Gitee From df2d0f47ed263b2284e4917d462f768d22463c7b Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Sun, 2 May 2021 14:28:13 +0800 Subject: [PATCH 24/28] doc: backward chap. 3 --- assignment-2/submission/18307130003/README.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index cbb037b..8ba1cac 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -231,8 +231,41 @@ class Relu(NumpyOp): #### 3.1 公式推导 +输入一个 $n\times d$ 的矩阵 $X$,对于 $X$ 中的每个元素 $X_{ij}$,算子 `Log` 的正向传播公式为: + +$$ +Y_{ij} = \log{X_{ij}} \tag{3.1} +$$ + +输出一个 $n\times d$ 的矩阵 $Y$。 + +对于梯度的反向传播,有 + +$$ +\begin{split} +\nabla{z} +&= \frac{\partial z}{\partial X}_{n\times d} \\ +&= \frac{\partial z}{\partial Y}_{n\times d} + \odot \frac{\partial Y}{\partial X}_{n\times d} +\end{split} \tag{3.2} +$$ + +其中,对于 $\frac{\partial Y}{\partial X}_{n\times d}$ 中的每个元素 ${Y'}_{ij}$,由 $(3.1)$ 有 + +$$ +{Y'}_{ij} = \frac{1}{X_{ij}} \tag{3.3} +$$ + +因此,对于 $\frac{\partial z}{\partial X}_{n\times d}$ 中的每个元素 ${Z'}_{ij}$,令 ${Z_Y}' = \frac{\partial z}{\partial Y}_{n\times d}$,由 $(3.2)$ 有 + +$$ +{Z'}_{ij} = \frac{{({Z_Y}')}_{ij}}{X_{ij}} \tag{3.4} +$$ + #### 3.2 代码实现 +为了防止 $X_{ij} = 0$ 时出现 $\log{X_{ij}} = -\infty$ 导致溢出,这里我们给 $X_{ij}$ 附加了一个 $\epsilon = 10^{-12}$ 的修正。 + ```python {.line-numbers} # numpy_fnn.py -- Gitee From 5acf27b257d05a30519c1e9d33960db67a46fbeb Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Sun, 2 May 2021 17:16:16 +0800 Subject: [PATCH 25/28] doc: backward chap. 4 --- assignment-2/submission/18307130003/README.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index 8ba1cac..a83d87d 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -303,6 +303,64 @@ class Log(NumpyOp): #### 4.1 公式推导 +输入一个 $n\times d$ 的矩阵 $X$,对于 $X$ 中的每个元素 $X_{ij}$,算子 `Softmax` 的正向传播公式为: + +$$ +Y_{ij} = \frac{e^{X_{ij}}}{\sum\limits_{k=1}^d e^{X_{ik}}} \tag{4.1} +$$ + +输出一个 $n\times d$ 的矩阵 $Y$。 + +对于梯度的反向传播,有 + +$$ +\begin{split} +\nabla{z} +&= \frac{\partial z}{\partial X}_{n\times d} +\end{split} \tag{4.2} +$$ + +其中,对于 $\frac{\partial z}{\partial X}_{n\times d}$ 中的每个元素 ${Z'}_{ij}$ 有 + +$$ +\begin{split} +{Z'}_{ij} +&= \frac{\partial z}{\partial X_{ij}} \\ +&= {(\frac{\partial z}{\partial Y_i})}_d + \cdot {(\frac{\partial Y_i}{\partial X_{ij}})}_d \\ +&= \frac{\partial z}{\partial Y_{ij}} + \cdot \frac{\partial Y_{ij}}{\partial X_{ij}} + + \sum\limits_{k=1,\,k\ne j}^d + \frac{\partial z}{\partial Y_{ik}} + \cdot \frac{\partial Y_{ik}}{\partial X_{ij}} \\ +&= \frac{\partial z}{\partial Y_{ij}} + \cdot \frac{\partial}{\partial X_{ij}}( + \frac{e^{X_{ij}}}{\sum\limits_{t=1}^d e^{X_{it}}} + ) + + \sum\limits_{k=1,\,k\ne j}^d + \frac{\partial z}{\partial Y_{ik}} + \cdot \frac{\partial}{\partial X_{ij}}( + \frac{e^{X_{ik}}}{\sum\limits_{t=1}^d e^{X_{it}}} + ) \\ +&= \frac{\partial z}{\partial Y_{ij}} + \cdot \frac{e^{X_{ij}}}{\sum\limits_{t=1}^d e^{X_{it}}} + \cdot (1 - \frac{e^{X_{ij}}}{\sum\limits_{t=1}^d e^{X_{it}}}) - + \sum\limits_{k=1,\,k\ne j}^d + \frac{\partial z}{\partial Y_{ik}} + \cdot \frac{e^{X_{ij}}}{\sum\limits_{t=1}^d e^{X_{it}}} + \cdot \frac{e^{X_{ik}}}{\sum\limits_{t=1}^d e^{X_{it}}} \\ +&= \frac{\partial z}{\partial Y_{ij}}\cdot Y_{ij}\cdot (1 - Y_{ij}) - + \sum\limits_{k=1,\,k\ne j}^d + \frac{\partial z}{\partial Y_{ik}}\cdot Y_{ij}\cdot Y_{ik} \\ +&= Y_{ij}\cdot ( + \frac{\partial z}{\partial Y_{ij}} - + \sum\limits_{k=1}^d + \frac{\partial z}{\partial Y_{ik}}\cdot Y_{ik} + ) +\end{split} +\tag{4.3} +$$ + #### 4.2 代码实现 ```python {.line-numbers} -- Gitee From 26d7bd86c117110f42f62235cbefb1c7c58b5b0c Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Sun, 2 May 2021 18:12:19 +0800 Subject: [PATCH 26/28] doc: report done --- assignment-2/submission/18307130003/README.md | 187 ++++++++++++++++++ .../18307130003/img/loss_value_1.png | Bin 0 -> 53631 bytes .../18307130003/img/loss_value_2.png | Bin 0 -> 44338 bytes .../18307130003/img/loss_value_3.png | Bin 0 -> 45915 bytes .../18307130003/img/loss_value_4.png | Bin 0 -> 37914 bytes .../submission/18307130003/numpy_mnist.py | 7 +- 6 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 assignment-2/submission/18307130003/img/loss_value_1.png create mode 100644 assignment-2/submission/18307130003/img/loss_value_2.png create mode 100644 assignment-2/submission/18307130003/img/loss_value_3.png create mode 100644 assignment-2/submission/18307130003/img/loss_value_4.png diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index a83d87d..61860fc 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -19,6 +19,24 @@ - [4. Softmax](#4-softmax) - [4.1 公式推导](#41-公式推导) - [4.2 代码实现](#42-代码实现) + - [函数 `mini_batch` 实现](#函数-mini_batch-实现) + - [实验过程与结果](#实验过程与结果) + - [1. 实验 1](#1-实验-1) + - [1.1 参数](#11-参数) + - [1.2 预测准确率](#12-预测准确率) + - [1.3 损失函数值](#13-损失函数值) + - [2. 实验 2](#2-实验-2) + - [2.1 参数](#21-参数) + - [2.2 预测准确率](#22-预测准确率) + - [2.3 损失函数值](#23-损失函数值) + - [3. 实验 3](#3-实验-3) + - [3.1 参数](#31-参数) + - [3.2 预测准确率](#32-预测准确率) + - [3.3 损失函数值](#33-损失函数值) + - [4. 实验 4](#4-实验-4) + - [4.1 参数](#41-参数) + - [4.2 预测准确率](#42-预测准确率) + - [4.3 损失函数值](#43-损失函数值) ## FNN 算子的反向传播 @@ -396,3 +414,172 @@ class Softmax(NumpyOp): y: np.ndarray = self.memory['y'] return y * (grad_y - (grad_y * y).sum(axis=1)[:, None]) ``` + +## 函数 `mini_batch` 实现 + +我们使用 NumPy 重写了函数 `mini_batch`,用于之后的训练。 + +```python {.line-numbers} +# numpy_mnist.py + +def mini_batch(dataset: List[Tuple[Any, int]], batch_size=128) -> np.ndarray: + ''' + Split the data and labels from the given dataset into batches. + + Args: + dataset: the given dataset + batch_size: the size of retrieved data + + Returns: + Batches of [data, labels] pair. + ''' + + data: np.ndarray = np.array([np.array(pair[0]) for pair in dataset]) + labels: np.ndarray = np.array([pair[1] for pair in dataset]) + + # Shuffle the dataset + size: int = len(dataset) + indices: np.ndarray = np.arange(size) + np.random.shuffle(indices) + + batches: List[Tuple[np.ndarray, np.ndarray]] = [] + for i in range(0, size, batch_size): + chunk: np.ndarray = indices[i:i+batch_size] + batches.append((data[chunk], labels[chunk])) + return batches +``` + +## 实验过程与结果 + +执行以下命令开始训练。 + +```bash +python ./numpy_mnist.py +``` + +### 1. 实验 1 + +#### 1.1 参数 + +```python {.line-numbers} +epoch_number = 3 +batch_size = 128 +learning_rate = 0.1 +``` + +#### 1.2 预测准确率 + +```text +[0] Accuracy: 0.9485 +[1] Accuracy: 0.9647 +[2] Accuracy: 0.9715 +``` + +#### 1.3 损失函数值 + +![Loss value 1](./img/loss_value_1.png) + +### 2. 实验 2 + +这次,我们调大训练轮数(`epoch_number`),观察预测准确率的变化。 + +#### 2.1 参数 + +```python {.line-numbers} +epoch_number = 10 +batch_size = 128 +learning_rate = 0.1 +``` + +#### 2.2 预测准确率 + +```text +[0] Accuracy: 0.9496 +[1] Accuracy: 0.9600 +[2] Accuracy: 0.9675 +[3] Accuracy: 0.9761 +[4] Accuracy: 0.9755 +[5] Accuracy: 0.9775 +[6] Accuracy: 0.9791 +[7] Accuracy: 0.9795 +[8] Accuracy: 0.9781 +[9] Accuracy: 0.9810 +``` + +可见,训练轮数越多,预测准确率越高,但到达一定准确率后开始波动,不再明显上升。 + +#### 2.3 损失函数值 + +![Loss value 2](./img/loss_value_2.png) + +可见,训练轮数越多,损失函数值越低,波动越小。 + +### 3. 实验 3 + +这次,我们调大批处理时每批数据大小(`batch_size`),观察预测准确率的变化。 + +#### 3.1 参数 + +```python {.line-numbers} +epoch_number = 10 +batch_size = 256 +learning_rate = 0.1 +``` + +#### 3.2 预测准确率 + +```text +[0] Accuracy: 0.9253 +[1] Accuracy: 0.9477 +[2] Accuracy: 0.9522 +[3] Accuracy: 0.9651 +[4] Accuracy: 0.9680 +[5] Accuracy: 0.9731 +[6] Accuracy: 0.9755 +[7] Accuracy: 0.9766 +[8] Accuracy: 0.9766 +[9] Accuracy: 0.9790 +``` + +可见,每批数据的大小越大,训练速度越慢,但最终达到的准确率没有明显变化。 + +#### 3.3 损失函数值 + +![Loss value 3](./img/loss_value_3.png) + +可见,每批数据的大小越大,损失函数值整体的波动越小。 + +### 4. 实验 4 + +这次,我们提高学习率(`learnin_rate`),观察预测准确率的变化。 + +#### 4.1 参数 + +```python {.line-numbers} +epoch_number = 10 +batch_size = 256 +learning_rate = 0.5 +``` + +#### 4.2 预测准确率 + +```text +[0] Accuracy: 0.9505 +[1] Accuracy: 0.9675 +[2] Accuracy: 0.9706 +[3] Accuracy: 0.9471 +[4] Accuracy: 0.9737 +[5] Accuracy: 0.8980 +[6] Accuracy: 0.9757 +[7] Accuracy: 0.9573 +[8] Accuracy: 0.9770 +[9] Accuracy: 0.9792 +``` + +可见,学习率越高,训练速度越快,但训练时的波动也可能较大。 + +#### 4.3 损失函数值 + +![Loss value 4](./img/loss_value_4.png) + +可见,学习率越高,损失函数值下降越快,但整体的波动也可能较大。 diff --git a/assignment-2/submission/18307130003/img/loss_value_1.png b/assignment-2/submission/18307130003/img/loss_value_1.png new file mode 100644 index 0000000000000000000000000000000000000000..ac76f374937aadf8ea57663647c3fbf8c8efc8fc GIT binary patch literal 53631 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 literal 0 HcmV?d00001 diff --git a/assignment-2/submission/18307130003/img/loss_value_2.png b/assignment-2/submission/18307130003/img/loss_value_2.png new file mode 100644 index 0000000000000000000000000000000000000000..9b09bfa0b05cc98cc41408c4703ca8b4c87ddf56 GIT binary patch literal 44338 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
?A2li;5xEEmW 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 literal 0 HcmV?d00001 diff --git a/assignment-2/submission/18307130003/numpy_mnist.py b/assignment-2/submission/18307130003/numpy_mnist.py index 101aac0..f992979 100644 --- a/assignment-2/submission/18307130003/numpy_mnist.py +++ b/assignment-2/submission/18307130003/numpy_mnist.py @@ -51,11 +51,12 @@ def numpy_run(): train_loss: List[float] = [] - epoch_number = 3 - learning_rate = 0.1 + epoch_number = 10 + batch_size = 256 + learning_rate = 0.5 for epoch in range(epoch_number): - for x, y in mini_batch(train_dataset): + for x, y in mini_batch(train_dataset, batch_size=batch_size): x: np.ndarray y: np.ndarray = one_hot(y) -- Gitee From db3bb81caff2ded95be3ad897fc90bf6d47d5516 Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Sun, 2 May 2021 19:27:04 +0800 Subject: [PATCH 27/28] doc: fix tex layout for gitee --- assignment-2/submission/18307130003/README.md | 115 ++++++++++++------ .../18307130003/img/equation_1.1.svg | 2 + .../18307130003/img/equation_1.2.svg | 2 + .../18307130003/img/equation_1.3.1.svg | 2 + .../18307130003/img/equation_1.3.svg | 2 + .../18307130003/img/equation_1.4.svg | 2 + .../18307130003/img/equation_1.5.svg | 2 + .../18307130003/img/equation_2.1.svg | 2 + .../18307130003/img/equation_2.2.svg | 2 + .../18307130003/img/equation_2.3.svg | 2 + .../18307130003/img/equation_2.4.svg | 2 + .../18307130003/img/equation_3.1.svg | 2 + .../18307130003/img/equation_3.2.svg | 2 + .../18307130003/img/equation_3.3.svg | 2 + .../18307130003/img/equation_3.4.svg | 2 + .../18307130003/img/equation_4.1.svg | 2 + .../18307130003/img/equation_4.2.svg | 2 + .../18307130003/img/equation_4.3.svg | 2 + 18 files changed, 110 insertions(+), 39 deletions(-) create mode 100644 assignment-2/submission/18307130003/img/equation_1.1.svg create mode 100644 assignment-2/submission/18307130003/img/equation_1.2.svg create mode 100644 assignment-2/submission/18307130003/img/equation_1.3.1.svg create mode 100644 assignment-2/submission/18307130003/img/equation_1.3.svg create mode 100644 assignment-2/submission/18307130003/img/equation_1.4.svg create mode 100644 assignment-2/submission/18307130003/img/equation_1.5.svg create mode 100644 assignment-2/submission/18307130003/img/equation_2.1.svg create mode 100644 assignment-2/submission/18307130003/img/equation_2.2.svg create mode 100644 assignment-2/submission/18307130003/img/equation_2.3.svg create mode 100644 assignment-2/submission/18307130003/img/equation_2.4.svg create mode 100644 assignment-2/submission/18307130003/img/equation_3.1.svg create mode 100644 assignment-2/submission/18307130003/img/equation_3.2.svg create mode 100644 assignment-2/submission/18307130003/img/equation_3.3.svg create mode 100644 assignment-2/submission/18307130003/img/equation_3.4.svg create mode 100644 assignment-2/submission/18307130003/img/equation_4.1.svg create mode 100644 assignment-2/submission/18307130003/img/equation_4.2.svg create mode 100644 assignment-2/submission/18307130003/img/equation_4.3.svg diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index 61860fc..6361105 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -1,3 +1,5 @@ + + # 实验报告 本次作业完成了选题 1 的实验内容,利用 NumPy 实现了一个 FNN 模型,并在 MNIST 数据集上进行了训练。 @@ -46,24 +48,28 @@ 输入一个 $n\times d$ 的矩阵 $X$ 和一个 $d\times d'$ 的矩阵 $W$,算子 `Matmul` 的正向传播公式为: -$$ + + +Equation 1.1 输出一个 $n\times d'$ 的矩阵 $Y$。 对于梯度的反向传播,有 -$$ + + +Equation 1.2 我们利用向量化(vectorization)进行对矩阵求导的求解: -$$ + + +Equation 1.3 这里 $\mathit{vec}(X_{m\times n})$ 表示向量 -$$ + + +Equation 1.3.1 $\otimes$ 表示 Kronecker 积,下标表示矩阵或向量的维度。 因此有 -$$ + + +Equation 1.4 类似 $(1.3)$ 的推导,同理可得 -$$ + + +Equation 1.5 #### 1.2 代码实现 @@ -161,7 +176,7 @@ class Matmul(NumpyOp): 输入一个 $n\times d$ 的矩阵 $X$,对于 $X$ 中的每个元素 $X_{ij}$,算子 `Relu` 的正向传播公式为: -$$ + + +Equation 2.1 输出一个 $n\times d$ 的矩阵 $Y$。 对于梯度的反向传播,有 -$$ + + +Equation 2.2 这里 $\odot$ 表示 Hadamard 积,即逐元素(element-wise)乘积。 -其中,对于 $\frac{\partial Y}{\partial X}_{n\times d}$ 中的每个元素 ${Y'}_{ij}$,由 $(2.1)$ 有 +其中,对于 $\frac{\partial Y}{\partial X}\_{n\times d}$ 中的每个元素 ${Y'}\_{ij}$,由 $(2.1)$ 有 -$$ + -因此,对于 $\frac{\partial z}{\partial X}_{n\times d}$ 中的每个元素 ${Z'}_{ij}$,令 ${Z_Y}' = \frac{\partial z}{\partial Y}_{n\times d}$,由 $(2.2)$ 有 +Equation 2.3 -$$ +因此,对于 $\frac{\partial z}{\partial X}\_{n\times d}$ 中的每个元素 ${Z'}\_{ij}$,令 ${Z\_Y}' = \frac{\partial z}{\partial Y}\_{n\times d}$,由 $(2.2)$ 有 + + + +Equation 2.4 #### 2.2 代码实现 @@ -251,34 +274,42 @@ class Relu(NumpyOp): 输入一个 $n\times d$ 的矩阵 $X$,对于 $X$ 中的每个元素 $X_{ij}$,算子 `Log` 的正向传播公式为: -$$ + + +Equation 3.1 输出一个 $n\times d$ 的矩阵 $Y$。 对于梯度的反向传播,有 -$$ + -其中,对于 $\frac{\partial Y}{\partial X}_{n\times d}$ 中的每个元素 ${Y'}_{ij}$,由 $(3.1)$ 有 +Equation 3.2 -$$ +其中,对于 $\frac{\partial Y}{\partial X}\_{n\times d}$ 中的每个元素 ${Y'}\_{ij}$,由 $(3.1)$ 有 + + + +Equation 3.3 -因此,对于 $\frac{\partial z}{\partial X}_{n\times d}$ 中的每个元素 ${Z'}_{ij}$,令 ${Z_Y}' = \frac{\partial z}{\partial Y}_{n\times d}$,由 $(3.2)$ 有 +因此,对于 $\frac{\partial z}{\partial X}\_{n\times d}$ 中的每个元素 ${Z'}\_{ij}$,令 ${Z\_Y}' = \frac{\partial z}{\partial Y}\_{n\times d}$,由 $(3.2)$ 有 -$$ + + +Equation 3.4 #### 3.2 代码实现 @@ -323,24 +354,28 @@ class Log(NumpyOp): 输入一个 $n\times d$ 的矩阵 $X$,对于 $X$ 中的每个元素 $X_{ij}$,算子 `Softmax` 的正向传播公式为: -$$ + + +Equation 4.1 输出一个 $n\times d$ 的矩阵 $Y$。 对于梯度的反向传播,有 -$$ + -其中,对于 $\frac{\partial z}{\partial X}_{n\times d}$ 中的每个元素 ${Z'}_{ij}$ 有 +Equation 4.2 -$$ +其中,对于 $\frac{\partial z}{\partial X}\_{n\times d}$ 中的每个元素 ${Z'}\_{ij}$ 有 + + + +Equation 4.3 #### 4.2 代码实现 diff --git a/assignment-2/submission/18307130003/img/equation_1.1.svg b/assignment-2/submission/18307130003/img/equation_1.1.svg new file mode 100644 index 0000000..048e56f --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_1.1.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_1.2.svg b/assignment-2/submission/18307130003/img/equation_1.2.svg new file mode 100644 index 0000000..33892d4 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_1.2.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_1.3.1.svg b/assignment-2/submission/18307130003/img/equation_1.3.1.svg new file mode 100644 index 0000000..005032e --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_1.3.1.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_1.3.svg b/assignment-2/submission/18307130003/img/equation_1.3.svg new file mode 100644 index 0000000..68d04c5 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_1.3.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_1.4.svg b/assignment-2/submission/18307130003/img/equation_1.4.svg new file mode 100644 index 0000000..d655eaa --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_1.4.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_1.5.svg b/assignment-2/submission/18307130003/img/equation_1.5.svg new file mode 100644 index 0000000..54e7cb1 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_1.5.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_2.1.svg b/assignment-2/submission/18307130003/img/equation_2.1.svg new file mode 100644 index 0000000..eb21ebb --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_2.1.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_2.2.svg b/assignment-2/submission/18307130003/img/equation_2.2.svg new file mode 100644 index 0000000..7edf73d --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_2.2.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_2.3.svg b/assignment-2/submission/18307130003/img/equation_2.3.svg new file mode 100644 index 0000000..bf68e43 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_2.3.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_2.4.svg b/assignment-2/submission/18307130003/img/equation_2.4.svg new file mode 100644 index 0000000..d6433c5 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_2.4.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_3.1.svg b/assignment-2/submission/18307130003/img/equation_3.1.svg new file mode 100644 index 0000000..391e193 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_3.1.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_3.2.svg b/assignment-2/submission/18307130003/img/equation_3.2.svg new file mode 100644 index 0000000..3d623c2 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_3.2.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_3.3.svg b/assignment-2/submission/18307130003/img/equation_3.3.svg new file mode 100644 index 0000000..6f5a883 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_3.3.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_3.4.svg b/assignment-2/submission/18307130003/img/equation_3.4.svg new file mode 100644 index 0000000..27682ed --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_3.4.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_4.1.svg b/assignment-2/submission/18307130003/img/equation_4.1.svg new file mode 100644 index 0000000..a4a9041 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_4.1.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_4.2.svg b/assignment-2/submission/18307130003/img/equation_4.2.svg new file mode 100644 index 0000000..2256e00 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_4.2.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assignment-2/submission/18307130003/img/equation_4.3.svg b/assignment-2/submission/18307130003/img/equation_4.3.svg new file mode 100644 index 0000000..2136ae5 --- /dev/null +++ b/assignment-2/submission/18307130003/img/equation_4.3.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file -- Gitee From 3acf06c7a23e2e518c2d296544c64adeb0c795de Mon Sep 17 00:00:00 2001 From: Hakula Chen Date: Sun, 2 May 2021 19:29:11 +0800 Subject: [PATCH 28/28] doc: fix typo --- assignment-2/submission/18307130003/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-2/submission/18307130003/README.md b/assignment-2/submission/18307130003/README.md index 6361105..13d2047 100644 --- a/assignment-2/submission/18307130003/README.md +++ b/assignment-2/submission/18307130003/README.md @@ -588,7 +588,7 @@ learning_rate = 0.1 ### 4. 实验 4 -这次,我们提高学习率(`learnin_rate`),观察预测准确率的变化。 +这次,我们提高学习率(`learning_rate`),观察预测准确率的变化。 #### 4.1 参数 -- Gitee

PBy2~p-VJ;tJrJc1Ft!)vU!Y2C0gnC0A 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 literal 0 HcmV?d00001 diff --git a/assignment-2/submission/18307130003/img/loss_value_3.png b/assignment-2/submission/18307130003/img/loss_value_3.png new file mode 100644 index 0000000000000000000000000000000000000000..482bd3f6c977c7acb1a0c6dfbf6acc687d92c47d GIT binary patch literal 45915 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 literal 0 HcmV?d00001 diff --git a/assignment-2/submission/18307130003/img/loss_value_4.png b/assignment-2/submission/18307130003/img/loss_value_4.png new file mode 100644 index 0000000000000000000000000000000000000000..268f540308806eb531e46cddbd64a4ebfaedcdc6 GIT binary patch literal 37914 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