提交 e669afd3 编写于 作者: W wizardforcel

2020-08-31 21:41:03

上级 1423cdfb
......@@ -70,7 +70,7 @@ def __init__(self, data, dtype=tf.float32):
self._sigma = None)
```
4. 给定输入数据的 SVD 用`fit`方法计算。 该方法定义了计算图,并执行该计算图以计算奇异值和正交矩阵 U。需要`self.data`来输入占位符`self._X``tf.svd`以降序返回形状[...,p]的 s`singular_values`)。 我们使用`tf.diag`将其转换为对角矩阵:
4. 给定输入数据的 SVD 用`fit`方法计算。 该方法定义了计算图,并执行该计算图以计算奇异值和正交矩阵`U`。需要`self.data`来输入占位符`self._X``tf.svd`以降序返回形状`[..., p]``s``singular_values`)。 我们使用`tf.diag`将其转换为对角矩阵:
```py
def fit(self):
......@@ -85,7 +85,7 @@ def fit(self):
self._u, self._singular_values, self._sigma = session.run([u, singular_values, sigma], feed_dict={self._X: self._data})
```
5. 现在我们有了 sigma 矩阵,正交 U 矩阵和奇异值,我们通过定义`reduce`方法来计算降维数据。 该方法需要两个输入参数之一`n_dimensions``keep_info``n_dimensions`参数表示我们要保留在降维数据集中的维数。 另一方面,`keep_info`参数确定我们要保留的信息的百分比(值为 0.8 表示我们要保留 80% 的原始数据)。 该方法创建一个切片 Sigma 矩阵的图,并计算降维数据集`Y[r]`
5. 现在我们有了`sigma`矩阵,正交`U`矩阵和奇异值,我们通过定义`reduce`方法来计算降维数据。 该方法需要两个输入参数之一`n_dimensions``keep_info``n_dimensions`参数表示我们要保留在降维数据集中的维数。 另一方面,`keep_info`参数确定我们要保留的信息的百分比(值为 0.8 表示我们要保留 80% 的原始数据)。 该方法创建一个切片 Sigma 矩阵的图,并计算降维数据集`Y[r]`
```py
def reduce(self, n_dimensions=None, keep_info=None):
......@@ -106,7 +106,7 @@ def reduce(self, n_dimensions=None, keep_info=None):
return session.run(pca, feed_dict={self._X: self._data})
```
6. 我们的`TF_PCA`类已准备就绪。 现在,我们将使用它来将 MNIST 数据从尺寸为 784(28 x 28)的每个输入减少为尺寸为 3 的每个点的新数据。这里,我们仅保留了 10% 的信息以便于查看,但是通常需要保留大约 80% 的信息:
6. 我们的`TF_PCA`类已准备就绪。 现在,我们将使用它来将 MNIST 数据从尺寸为 784(`28 x 28`)的每个输入减少为尺寸为 3 的每个点的新数据。这里,我们仅保留了 10% 的信息以便于查看,但是通常需要保留大约 80% 的信息:
```py
tf_pca.fit()
......@@ -134,11 +134,11 @@ ax.scatter(pca[:, 0], pca[:, 1],pca[:, 2], c=colors)
# 这个怎么运作...
前面的代码对 MNIST 图像执行降维。 每个原始图像的尺寸为 28 x 28; 使用 PCA 方法,我们可以将其减小到较小的尺寸。 通常,对于图像数据,降维是必要的。 之所以如此,是因为图像很大,并且包含大量的冗余数据。
前面的代码对 MNIST 图像执行降维。 每个原始图像的尺寸为`28 x 28`; 使用 PCA 方法,我们可以将其减小到较小的尺寸。 通常,对于图像数据,降维是必要的。 之所以如此,是因为图像很大,并且包含大量的冗余数据。
# 还有更多...
TensorFlow 提供了一种称为**嵌入**的技术,该技术是将对象映射到向量中。 TensorBoard 的嵌入式投影仪允许我们以交互方式可视化模型中的嵌入。 嵌入式投影仪提供了三种降低尺寸的方法:PCA,t-SNE 和自定义。 我们可以使用 TensorBoard 的 Embedding Projector 实现与上一个类似的结果。 我们需要从`tensorflow.contrib.tensorboard.plugins`导入`projector`类,以从`tensorflow.contrib.tensorboard.plugins` import `projector`进行相同的操作。 我们可以通过三个简单的步骤来做到这一点:
TensorFlow 提供了一种称为**嵌入**的技术,该技术是将对象映射到向量中。 TensorBoard 的嵌入式投影仪允许我们以交互方式可视化模型中的嵌入。 嵌入式投影仪提供了三种降低尺寸的方法:PCA,t-SNE 和自定义。 我们可以使用 TensorBoard 的嵌入投影器实现与上一个类似的结果。 我们需要从`tensorflow.contrib.tensorboard.plugins`导入`projector`类,以从`tensorflow.contrib.tensorboard.plugins`导入`projector`进行相同的操作。 我们可以通过三个简单的步骤来做到这一点:
1. 加载要探索其嵌入的数据:
......@@ -174,7 +174,7 @@ with tf.Session() as sess:
projector.visualize_embeddings(tf.summary.FileWriter(LOG_DIR), config)
```
嵌入已准备就绪,现在可以使用 TensorBoard 看到。 通过 CLI `tensorboard --logdir=log`启动 TensorBoard,在 Web 浏览器中打开 TensorBoard,然后转到 EMBEDDINGS 选项卡。 这是使用 PCA 的 TensorBoard 投影,前三个主要成分为轴:
嵌入已准备就绪,现在可以使用 TensorBoard 看到。 通过 CLI `tensorboard --logdir=log`启动 TensorBoard,在 Web 浏览器中打开 TensorBoard,然后转到`EMBEDDINGS`选项卡。 这是使用 PCA 的 TensorBoard 投影,前三个主要成分为轴:
![](img/0138c6e3-6c07-415d-a74c-f080e9d27718.png)
......@@ -200,7 +200,7 @@ K 均值算法以以下方式工作:
# 做好准备
我们将使用 TensorFlow `KmeansClustering` Estimator 类来实现 K 均值。 它在[这个链接](https://github.com/tensorflow/tensorflow/blob/r1.3/tensorflow/contrib/learn/python/learn/estimators/kmeans.py)中定义。它创建一个模型来运行 K 均值和推理。 根据 TensorFlow 文档,一旦创建了`KmeansClustering`类对象,就可以使用以下`__init__`方法实例化该对象:
我们将使用 TensorFlow `KmeansClustering`估计器类来实现 K 均值。 它在[这个链接](https://github.com/tensorflow/tensorflow/blob/r1.3/tensorflow/contrib/learn/python/learn/estimators/kmeans.py)中定义。它创建一个模型来运行 K 均值和推理。 根据 TensorFlow 文档,一旦创建了`KmeansClustering`类对象,就可以使用以下`__init__`方法实例化该对象:
```py
__init__(
......@@ -286,7 +286,7 @@ plt.ylabel('Sepia Width')
![](img/30876097-6b58-458e-b7f7-122110365b5f.png)
4. 我们可以看到在数据中没有明显可见的聚类。 现在我们定义`input_fn`,它将用于提供`fit`方法。 我们的输入函数返回一个 TensorFlow 常数,该常数被分配了 x 的值和形状,并且类型为`float`
4. 我们可以看到在数据中没有明显可见的聚类。 现在我们定义`input_fn`,它将用于提供`fit`方法。 我们的输入函数返回一个 TensorFlow 常数,该常数被分配了`x`的值和形状,并且类型为`float`
```py
def input_fn():
......@@ -337,13 +337,13 @@ ScatterPlot(x[:,0], x[:,1], assignments, clusters)
# 这个怎么运作...
前面的配方使用 TensorFlow 的 K 均值聚类估计器将给定数据聚类为聚类。 在这里,由于我们知道集群的数量,我们决定保留`num_clusters=3`,但是在大多数情况下,如果使用未标记的数据,则永远无法确定存在多少集群。 可以使用弯头法确定最佳簇数。 该方法基于以下原则:我们应选择能减少**平方误差和****SSE**)距离的簇数。 如果`k`是簇数,则随着`k`增加,SSE 减少,SSE = 0; 当`k`等于数据点数时,每个点都是其自己的簇。 我们想要一个`k`较低的值,以使 SSE 也较低。 在 TensorFlow 中,我们可以使用`KmeansClustering`类中定义的`score()`方法找到 SSE; 该方法将距离的总和返回到最近的聚类:
前面的配方使用 TensorFlow 的 K 均值聚类估计器将给定数据聚类为聚类。 在这里,由于我们知道集群的数量,我们决定保留`num_clusters=3`,但是在大多数情况下,如果使用未标记的数据,则永远无法确定存在多少集群。 可以使用弯头法确定最佳簇数。 该方法基于以下原则:我们应选择能减少**平方误差和****SSE**)距离的簇数。 如果`k`是簇数,则随着`k`增加,SSE 减少,`SSE = 0`; 当`k`等于数据点数时,每个点都是其自己的簇。 我们想要一个`k`较低的值,以使 SSE 也较低。 在 TensorFlow 中,我们可以使用`KmeansClustering`类中定义的`score()`方法找到 SSE; 该方法将距离的总和返回到最近的聚类:
```py
sum_distances = kmeans.score(input_fn=input_fn, steps=100)
```
对于虹膜数据,如果我们针对不同的`k`值绘制 SSE,则可以看到对于`k = 3`而言,SSE 的方差最高; 之后,它开始减小,因此肘点为`k = 3`
对于鸢尾数据,如果我们针对不同的`k`值绘制 SSE,则可以看到对于`k = 3`而言,SSE 的方差最高; 之后,它开始减小,因此肘点为`k = 3`
![](img/fc2034f7-b673-471b-8ba0-8b1fdb58810c.png)
......@@ -367,7 +367,7 @@ K 均值聚类非常流行,因为它快速,简单且健壮。 它还有一
**自组织地图****SOM**),有时也称为 **Kohonen 网络****胜者通吃单元****WTU**),是一种非常特殊的神经网络,受人脑的独特特征驱动。 在我们的大脑中,不同的感觉输入以拓扑有序的方式表示。 与其他神经网络不同,神经元并非都通过权重相互连接,而是会影响彼此的学习。 SOM 的最重要方面是神经元以拓扑方式表示学习的输入。
在 SOM 中,神经元通常放置在(1D 或 2D)晶格的节点上。 更大的尺寸也是可能的,但实际上很少使用。 晶格中的每个神经元都通过权重矩阵连接到所有输入单元。 在这里,您可以看到一个具有 3 x 4(12 个神经元)和七个输入的 SOM。 为了清楚起见,仅显示将所有输入连接到一个神经元的权重向量。 在这种情况下,每个神经元将具有七个元素,从而形成大小为(12 x 7)的组合权重矩阵:
在 SOM 中,神经元通常放置在(1D 或 2D)晶格的节点上。 更大的尺寸也是可能的,但实际上很少使用。 晶格中的每个神经元都通过权重矩阵连接到所有输入单元。 在这里,您可以看到一个具有`3 x 4`(12 个神经元)和七个输入的 SOM。 为了清楚起见,仅显示将所有输入连接到一个神经元的权重向量。 在这种情况下,每个神经元将具有七个元素,从而形成大小为(`12 x 7`)的组合权重矩阵:
![](img/22a4a2c8-9e42-496b-bf1f-e66e0c5bf09d.png)
......@@ -716,7 +716,7 @@ labels=self._X, logits=reconstructed_input))
return obj
```
4. 还有其他帮助程序功能可计算 logit 错误并从网络返回重建的图像:
4. 还有其他帮助程序功能可计算对率误差并从网络返回重建的图像:
```py
def set_session(self, session):
......@@ -804,7 +804,7 @@ for fig, row in zip([Xtest_noisy,out], axarr):
# 使用 RBM 的推荐系统
网上零售商广泛使用推荐系统向客户推荐产品。 例如,亚马逊会告诉您购买此商品的其他客户对什么感兴趣,或者 Netflix 根据您所观看的内容以及有相同兴趣的其他 Netflix 用户所观看的内容推荐电视连续剧和电影。 这些推荐器系统在协作筛选的基础上工作。 在协作过滤中,系统根据用户的过去行为来构建模型。 我们将使用上一个食谱中的 RBM 构建一个使用协作过滤来推荐电影的推荐器系统。 这项工作中的一个重要挑战是,大多数用户不会对所有产品/电影进行评分,因此大多数数据都将丢失。 如果有 M 个产品和 N 个用户,则我们需要构建一个数组 N x M,其中包含用户的已知等级并将所有未知值设为零。
网上零售商广泛使用推荐系统向客户推荐产品。 例如,亚马逊会告诉您购买此商品的其他客户对什么感兴趣,或者 Netflix 根据您所观看的内容以及有相同兴趣的其他 Netflix 用户所观看的内容推荐电视连续剧和电影。 这些推荐器系统在协作筛选的基础上工作。 在协作过滤中,系统根据用户的过去行为来构建模型。 我们将使用上一个食谱中的 RBM 构建一个使用协作过滤来推荐电影的推荐器系统。 这项工作中的一个重要挑战是,大多数用户不会对所有产品/电影进行评分,因此大多数数据都将丢失。 如果有 M 个产品和 N 个用户,则我们需要构建一个数组`N x M`,其中包含用户的已知等级并将所有未知值设为零。
# 做好准备
......@@ -820,7 +820,7 @@ n = 20 # Number of Hidden units
recommender = rbm.RBM(m,n)
```
2. 我们使用 Pandas 合并和`groupby`命令创建了一个列表`trX`,该列表包含大约 1,000 个用户的规范化电影评分。 列表的大小为 1000 x3883。我们使用它来训练我们的 RBM:
2. 我们使用 Pandas 合并和`groupby`命令创建了一个列表`trX`,该列表包含大约 1,000 个用户的规范化电影评分。 列表的大小为`1000 x 3883`。我们使用它来训练我们的 RBM:
```py
Xtrain = np.array(trX)
......@@ -990,7 +990,7 @@ for i, size in enumerate(RBM_hidden_sizes):
input_size = size
```
这将生成三个 RBM:第一个 RBM 具有 2304(48×48)个输入和 1500 个隐藏单元,第二个 RBM 具有 1500 个输入和 700 个隐藏单元,最后第三个 RBM 具有 700 个输入和 400 个隐藏单元。
这将生成三个 RBM:第一个 RBM 具有 2304(`48×48`)个输入和 1500 个隐藏单元,第二个 RBM 具有 1500 个输入和 700 个隐藏单元,最后第三个 RBM 具有 700 个输入和 400 个隐藏单元。
6. 我们逐一训练每个 RBM。 该技术也称为**贪婪训练**。 在原始论文中,用于在 MNIST 上训练每个 RBM 的时期数是 30,因此在这里,增加时期也应会改善网络的性能:
......@@ -1132,7 +1132,7 @@ with tf.Session() as sess:
RBM 使用无监督学习来学习模型的隐藏表示/功能,然后对与预训练 RBM 一起添加的全连接层进行微调。
这里的精度在很大程度上取决于图像表示。 在前面的食谱中,我们没有使用图像处理,仅使用了 0 到 1 之间缩放的灰度图像。但是,如果我们按照以下论文所述添加图像处理,[则会进一步提高精度](http://deeplearning.net/wp-content/uploads/2013/03/dlsvm.pdf)。 因此,我们在`preprocess_data`函数中将每个图像乘以 100.0 / 255.0,然后将以下几行代码添加到主代码中:
这里的精度在很大程度上取决于图像表示。 在前面的食谱中,我们没有使用图像处理,仅使用了 0 到 1 之间缩放的灰度图像。但是,如果我们按照以下论文所述添加图像处理,[则会进一步提高精度](http://deeplearning.net/wp-content/uploads/2013/03/dlsvm.pdf)。 因此,我们在`preprocess_data`函数中将每个图像乘以 100.0/255.0,然后将以下几行代码添加到主代码中:
```py
std_image = np.std(X_train, axis=0)
......
......@@ -6,7 +6,7 @@
* 稀疏自编码器
* 去噪自编码器
* 卷积汽车编码器
* 堆叠式自编码器
* 式自编码器
# 介绍
......@@ -200,11 +200,11 @@ for fig, row in zip([Xtest,out], axarr):
# 还有更多...
诸如 PCA 之类的自编码器可以用于降维,但是 PCA 仅可以表示线性变换,但是我们可以在自编码器中使用非线性激活函数,从而在编码中引入非线性。 这是从 Hinton 论文复制的结果,该结果使用*神经网络*降低了数据的维数。 该结果将 PCA(A)的结果与堆叠式 RBM 作为具有 784-1000-500-250-2 架构的自编码器的结果进行了比较:
诸如 PCA 之类的自编码器可以用于降维,但是 PCA 仅可以表示线性变换,但是我们可以在自编码器中使用非线性激活函数,从而在编码中引入非线性。 这是从 Hinton 论文复制的结果,该结果使用*神经网络*降低了数据的维数。 该结果将 PCA(A)的结果与式 RBM 作为具有 784-1000-500-250-2 架构的自编码器的结果进行了比较:
![](img/e7b06856-5f73-46b2-a430-7d8701770449.png)
正如我们稍后将看到的,当使用堆叠式自编码器制作自编码器时,每个自编码器最初都经过单独的预训练,然后对整个网络进行微调以获得更好的性能。
正如我们稍后将看到的,当使用式自编码器制作自编码器时,每个自编码器最初都经过单独的预训练,然后对整个网络进行微调以获得更好的性能。
# 稀疏自编码器
......@@ -249,7 +249,7 @@ mnist = input_data.read_data_sets("MNIST_data/")
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
```
3. 定义`SparseAutoEncoder`类,它与前面的食谱中的 autoencoder 类非常相似,除了引入了 KL 散度损失之外:
3. 定义`SparseAutoEncoder`类,它与前面的食谱中的`AutoEncoder`类非常相似,除了引入了 KL 散度损失之外:
```py
def kl_div(self, rho, rho_hat):
......@@ -423,7 +423,7 @@ for fig, row in zip([Xtest,out], axarr):
# 准备好
去噪自编码器还将具有 KL 散度惩罚项; 它在两个主要方面与先前食谱的稀疏自编码器有所不同。 首先,`n_hidden > m`瓶颈层中的隐藏单元数大于输入层 m 中的单元数`n_hidden > m`。 其次,编码器的输入已损坏。 为了在 TensorFlow 中做到这一点,我们添加了 invalid 函数,这给输入增加了噪音:
去噪自编码器还将具有 KL 散度惩罚项; 它在两个主要方面与先前食谱的稀疏自编码器有所不同。 首先,`n_hidden > m`瓶颈层中的隐藏单元数大于输入层`m`中的单元数`n_hidden > m`。 其次,编码器的输入已损坏。 为了在 TensorFlow 中做到这一点,我们添加了`invalid`函数,这给输入增加了噪音:
```py
def corruption(x, noise_factor = 0.3): #corruption of the input
......@@ -537,7 +537,7 @@ obj.append(ob)
return obj
```
也可以向自编码器对象添加噪声。 在这种情况下,您将使用类`self._X_noisy = self.corrupt(self._X) * 0.3 + self._X * (1 - 0.3)`中定义的损坏方法,并且 fit 方法也将更改为以下内容:
也可以向自编码器对象添加噪声。 在这种情况下,您将使用类`self._X_noisy = self.corrupt(self._X) * 0.3 + self._X * (1 - 0.3)`中定义的损坏方法,并且`fit`方法也将更改为以下内容:
```py
def fit(self, X, epochs = 1, batch_size = 100):
......@@ -793,13 +793,13 @@ sess.close()
3. <https://arxiv.org/pdf/1511.00561.pdf>
4. <https://github.com/arahusky/Tensorflow-Segmentation>
# 堆叠式自编码器
# 式自编码器
到目前为止,涵盖的自编码器(CAE 除外)仅由单层编码器和单层解码器组成。 但是,我们可能在编码器和解码器网络中具有多层; 使用更深的编码器和解码器网络可以使自编码器代表复杂的功能。 这样获得的结构称为堆叠式自编码器(**深度自编码器**); 由一个编码器提取的特征将作为输入传递到下一个编码器。 可以将堆叠式自编码器作为一个整体网络进行训练,以最大程度地减少重构误差,或者可以首先使用您先前学习的无监督方法对每个单独的编码器/解码器网络进行预训练,然后对整个网络进行微调。 已经指出,通过预训练,也称为贪婪分层训练,效果更好。
到目前为止,涵盖的自编码器(CAE 除外)仅由单层编码器和单层解码器组成。 但是,我们可能在编码器和解码器网络中具有多层; 使用更深的编码器和解码器网络可以使自编码器代表复杂的功能。 这样获得的结构称为栈式自编码器(**深度自编码器**); 由一个编码器提取的特征将作为输入传递到下一个编码器。 可以将栈式自编码器作为一个整体网络进行训练,以最大程度地减少重构误差,或者可以首先使用您先前学习的无监督方法对每个单独的编码器/解码器网络进行预训练,然后对整个网络进行微调。 已经指出,通过预训练,也称为贪婪分层训练,效果更好。
# 准备好
在配方中,我们将使用贪婪分层方法来训练堆叠式自编码器; 为了简化任务,我们将使用共享权重,因此相应的编码器/解码器权重将相互转换。
在配方中,我们将使用贪婪分层方法来训练式自编码器; 为了简化任务,我们将使用共享权重,因此相应的编码器/解码器权重将相互转换。
# 怎么做...
......@@ -867,7 +867,7 @@ class StackedAutoEncoder(object):
self.out[layer] = self.one_pass(self._X[prev_layer], self._W['E'+layer], self._b['E'+layer], self._b['D'+layer])
```
5. 我们建立计算图以对整个堆叠式自编码器进行微调。 为此,我们使用类方法`encoder``decoder`
5. 我们建立计算图以对整个式自编码器进行微调。 为此,我们使用类方法`encoder``decoder`
```py
self.y = self.encoder(self._X_noisy,N) #Encoder output
......@@ -880,7 +880,7 @@ self._loss = tf.reduce_mean(tf.pow(error, 2))
self._opt = optimizer.minimize(self._loss)
```
6. 最后,我们定义类方法`fit`,以执行每个自编码器的分批预训练,然后进行微调。 在进行预训练时,我们使用未损坏的输入,而对于微调,我们使用损坏的输入。 这使我们能够使用堆叠式自编码器甚至从嘈杂的输入中进行重构:
6. 最后,我们定义类方法`fit`,以执行每个自编码器的分批预训练,然后进行微调。 在进行预训练时,我们使用未损坏的输入,而对于微调,我们使用损坏的输入。 这使我们能够使用式自编码器甚至从嘈杂的输入中进行重构:
```py
def fit(self, Xtrain, Xtr_noisy, layers, epochs = 1, batch_size = 100):
......@@ -956,7 +956,7 @@ def one_pass(self, X, W, b, c):
return h
```
8. 我们使用 Denoising 自编码器配方中定义的破坏功能来破坏图像,最后创建一个 StackAutoencoder 并对其进行训练:
8. 我们使用降噪自编码器配方中定义的破坏功能来破坏图像,最后创建一个`StackAutoencoder`并对其进行训练:
```py
Xtrain = trX.astype(np.float32)
......@@ -997,7 +997,7 @@ plt.ylabel('Fine Tuning Reconstruction Error')
# 这个怎么运作...
堆叠式自编码器上进行的实验表明,应以较低的学习率值进行预训练。 这样可以确保在微调期间具有更好的收敛性和性能。
式自编码器上进行的实验表明,应以较低的学习率值进行预训练。 这样可以确保在微调期间具有更好的收敛性和性能。
# 还有更多...
......@@ -1005,7 +1005,7 @@ plt.ylabel('Fine Tuning Reconstruction Error')
# 也可以看看
* [关于堆叠式自编码器的一个不错的教程](http://ufldl.stanford.edu/wiki/index.php/Stacked_Autoencoders)
* [关于式自编码器的一个不错的教程](http://ufldl.stanford.edu/wiki/index.php/Stacked_Autoencoders)
* `Schwenk, Holger. "The diabolo classifier." Neural Computation 10.8 (1998): 2175-2200.`
* `Sakurada, Mayu, and Takehisa Yairi. "Anomaly detection using autoencoders with nonlinear dimensionality reduction." Proceedings of the MLSDA 2014 2nd Workshop on Machine Learning for Sensory Data Analysis. ACM, 2014.`
* [堆叠式自编码器的酷 TensorBoard 可视化和实现](https://github.com/cmgreen210/TensorFlowDeepAutoencoder)
\ No newline at end of file
* [栈式自编码器的酷 TensorBoard 可视化和实现](https://github.com/cmgreen210/TensorFlowDeepAutoencoder)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册