k-近邻算法的一个局限性是,为了进行预测,您必须维护一个大型的训练样本数据库。
学习向量量化算法通过学习一个小的模式子集来最好地表示训练数据,从而解决了这个问题。
在本教程中,您将学习如何从零开始使用 Python 实现学习向量量化算法。
完成本教程后,您将了解:
- 如何从训练数据集中学习一组码本向量。
- 如何使用学习到的码本向量进行预测。
- 如何将学习向量量化应用于实际的预测建模问题。
立即开始您的项目,阅读我的新书《从零开始的机器学习算法》,其中包含分步教程以及所有示例的Python源代码文件。
让我们开始吧。
- 更新于 2017 年 1 月:更改了 `cross_validation_split()` 中 `fold_size` 的计算,使其始终为整数。修复了 Python 3 的问题。
- 2018 年 8 月更新:测试并更新以与 Python 3.6 配合使用。

如何从零开始用 Python 实现学习向量量化
照片由 Tony Faiola 拍摄,保留部分权利。
描述
本节将简要介绍学习向量量化算法以及我们将在本教程中使用的电离层分类问题
学习向量量化
学习向量量化 (LVQ) 算法与 k-近邻算法非常相似。
预测是通过在模式库中查找最佳匹配来完成的。不同之处在于,模式库是从训练数据中学习到的,而不是使用训练模式本身。
模式库称为码本向量,每个模式称为一个码本。码本向量被初始化为从训练数据集中随机选择的值。然后,在多个训练周期中,它们会使用学习算法进行调整,以最好地总结训练数据。
学习算法一次显示一条训练记录,找到码本向量中的最佳匹配单元,如果它们具有相同的类别,则将其移近训练记录,如果具有不同的类别,则将其移远。
准备好后,码本向量将用于使用 k=1 的 k-近邻算法进行预测。
该算法是为分类预测建模问题开发的,但也可以用于回归问题。
电离层数据集
电离层数据集根据雷达回波数据预测电离层的结构。
每个实例都描述了来自大气的雷达回波的属性,任务是预测电离层中是否存在结构。
有 351 个实例和 34 个数值输入变量,每对雷达脉冲有 17 对,通常具有相同的 0-1 尺度。类别值是一个字符串,值为“g”表示良好回波或“b”表示不良回波。
使用预测观察次数最多的类别的零规则算法,可以达到 64.286% 的基线准确率。
您可以在 UCI 机器学习数据库 上了解更多信息并下载数据集。
下载数据集并将其放在当前工作目录中,文件名为 ionosphere.csv。
教程
本教程分为 4 部分
- 欧几里得距离。
- 最佳匹配单元。
- 训练码本向量。
- 电离层案例研究。
这些步骤将为实现 LVQ 算法并将其应用于您自己的预测建模问题奠定基础。
1. 欧几里得距离
第一个需要进行的步骤是计算数据集中两个行之间的距离。
数据行大部分由数字组成,计算两个行或数字向量之间距离的简单方法是画一条直线。这在二维或三维中是有意义的,并且可以很好地扩展到更高维度。
我们可以使用欧几里得距离度量来计算两个向量之间的直线距离。它计算为两个向量之间差的平方和的平方根。
1 |
distance = sqrt( sum( (x1_i - x2_i)^2 ) |
其中 x1 是第一行数据,x2 是第二行数据,i 是特定列的索引,我们在所有列上进行求和。
使用欧几里得距离,值越小,两个记录越相似。值为 0 表示两个记录之间没有差异。
下面是一个名为 euclidean_distance() 的函数,它在 Python 中实现了这一点。
1 2 3 4 5 6 |
# 计算两个向量之间的欧几里得距离 def euclidean_distance(row1, row2): distance = 0.0 for i in range(len(row1)-1): distance += (row1[i] - row2[i])**2 return sqrt(distance) |
您可以看到该函数假定每行中的最后一列是输出值,该值在距离计算中被忽略。
我们可以使用一个小的人为构造的分类数据集来测试此距离函数。我们将此数据集使用几次,以构建 LVQ 算法所需的元素。
1 2 3 4 5 6 7 8 9 10 11 |
X1 X2 Y 2.7810836 2.550537003 0 1.465489372 2.362125076 0 3.396561688 4.400293529 0 1.38807019 1.850220317 0 3.06407232 3.005305973 0 7.627531214 2.759262235 1 5.332441248 2.088626775 1 6.922596716 1.77106367 1 8.675418651 -0.242068655 1 7.673756466 3.508563011 1 |
将所有这些放在一起,我们可以编写一个小的示例来测试我们的距离函数,方法是打印第一个行与所有其他行之间的距离。我们期望第一个行与自身之间的距离为 0,这是一个很好的检查点。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
from math import sqrt # 计算两个向量之间的欧几里得距离 def euclidean_distance(row1, row2): distance = 0.0 for i in range(len(row1)-1): distance += (row1[i] - row2[i])**2 return sqrt(distance) # 测试距离函数 dataset = [[2.7810836,2.550537003,0], [1.465489372,2.362125076,0], [3.396561688,4.400293529,0], [1.38807019,1.850220317,0], [3.06407232,3.005305973,0], [7.627531214,2.759262235,1], [5.332441248,2.088626775,1], [6.922596716,1.77106367,1], [8.675418651,-0.242068655,1], [7.673756466,3.508563011,1]] row0 = dataset[0] for row in dataset: distance = euclidean_distance(row0, row) print(distance) |
运行此示例将打印第一个行与数据集中每个行(包括自身)之间的距离。
1 2 3 4 5 6 7 8 9 10 |
0.0 1.32901739153 1.94946466557 1.55914393855 0.535628072194 4.85094018699 2.59283375995 4.21422704263 6.52240998823 4.98558538245 |
现在是时候使用距离计算来定位数据集中的最佳匹配单元了。
2. 最佳匹配单元
最佳匹配单元或 BMU 是与新数据最相似的码本向量。
要在数据集中定位新数据的 BMU,我们必须首先计算每个码本与新数据之间的距离。我们可以使用上面提供的距离函数来做到这一点。
一旦计算出距离,我们必须按距离对所有码本进行排序(降序)。然后我们可以返回第一个或最相似的码本向量。
我们可以通过将每个记录的距离作为元组进行跟踪,按距离对元组列表进行排序(降序),然后检索 BMU 来实现此目的。
下面是一个名为 get_best_matching_unit() 的函数,它实现了这一点。
1 2 3 4 5 6 7 8 |
# 定位最佳匹配单元 def get_best_matching_unit(codebooks, test_row): distances = list() for codebook in codebooks: dist = euclidean_distance(codebook, test_row) distances.append((codebook, dist)) distances.sort(key=lambda tup: tup[1]) return distances[0][0] |
您可以看到,在上一节中开发的 euclidean_distance() 函数用于计算每个码本与新的 test_row 之间的距离。
码本和距离元组的列表被排序,其中使用了自定义键,确保元组中的第二个项 (tup[1]) 用于排序操作。
最后,返回作为 BMU 的顶部或最相似的码本向量。
我们可以使用上一节准备的小型人为构造数据集来测试此函数。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
from math import sqrt # 计算两个向量之间的欧几里得距离 def euclidean_distance(row1, row2): distance = 0.0 for i in range(len(row1)-1): distance += (row1[i] - row2[i])**2 return sqrt(distance) # 定位最佳匹配单元 def get_best_matching_unit(codebooks, test_row): distances = list() for codebook in codebooks: dist = euclidean_distance(codebook, test_row) distances.append((codebook, dist)) distances.sort(key=lambda tup: tup[1]) return distances[0][0] # 测试最佳匹配单元函数 dataset = [[2.7810836,2.550537003,0], [1.465489372,2.362125076,0], [3.396561688,4.400293529,0], [1.38807019,1.850220317,0], [3.06407232,3.005305973,0], [7.627531214,2.759262235,1], [5.332441248,2.088626775,1], [6.922596716,1.77106367,1], [8.675418651,-0.242068655,1], [7.673756466,3.508563011,1]] test_row = dataset[0] bmu = get_best_matching_unit(dataset, test_row) print(bmu) |
运行此示例将打印数据集中 BMU 到第一个记录。正如预期的那样,第一个记录与自身最相似,并且位于列表的顶部。
1 |
[2.7810836, 2.550537003, 0] |
使用一组码本向量进行预测也是如此。
我们使用 1-近邻算法。也就是说,对于我们想要进行预测的每个新模式,我们找到集合中最相似的码本向量,并返回其关联的类别值。
既然我们知道如何从一组码本向量中获取最佳匹配单元,我们就需要学习如何训练它们。
3. 训练码本向量
训练一组码本向量的第一步是初始化该集合。
我们可以使用训练数据集中随机特征构建的模式来初始化它。
下面是一个名为 random_codebook() 的函数,它实现了这一点。从训练数据中随机选择输入和输出特征。
1 2 3 4 5 6 |
# 创建一个随机码本向量 def random_codebook(train): n_records = len(train) n_features = len(train[0]) codebook = [train[randrange(n_records)][i] for i in range(n_features)] return codebook |
在将码本向量初始化为随机集之后,必须对它们进行调整以最好地总结训练数据。
这是通过迭代完成的。
- 训练周期:在最高级别,该过程重复固定数量的训练数据暴露周期。
- 训练数据集:在每个训练周期中,一次使用一个训练模式来更新码本向量集。
- 模式特征:对于给定的训练模式,会更新最佳匹配码本向量的每个特征,以使其更近或更远。
为每个训练模式找到最佳匹配单元,并且只有这个最佳匹配单元被更新。训练模式与 BMU 之间的差异计算为误差。比较类别值(假定为列表中的最后一个值)。如果它们匹配,则将误差加到 BMU 上,使其更接近训练模式;否则,将其减去,使其远离。
BMU 被调整的量由学习率控制。这是对所有 BMU 更改量的一个加权。例如,学习率为 0.3 意味着 BMU 只移动了训练模式和 BMU 之间误差或差值的 30%。
此外,学习率会进行调整,使其在第一个训练周期中产生最大影响,并随着训练的进行而产生较小的影响,直到在最后一个训练周期中产生最小影响。这称为线性衰减学习率计划,也可用于人工神经网络。
我们可以将这种学习率衰减按训练周期数进行总结,如下所示:
1 |
rate = learning_rate * (1.0 - (epoch/total_epochs)) |
我们可以通过假设学习率为 0.3 且训练周期数为 10 来测试此方程。每个训练周期的学习率将如下所示:
1 2 3 4 5 6 7 8 9 10 11 |
训练周期 有效学习率 0 0.3 1 0.27 2 0.24 3 0.21 4 0.18 5 0.15 6 0.12 7 0.09 8 0.06 9 0.03 |
我们可以将所有这些整合起来。下面是一个名为 train_codebooks() 的函数,它在给定训练数据集的情况下实现了训练一组码本向量的过程。
该函数除了训练数据集外,还接受三个附加参数:要创建和训练的码本向量的数量、初始学习率以及训练码本向量的训练周期数。
您还可以看到该函数跟踪每个训练周期的均方误差,并打印一条消息,显示训练周期数、有效学习率和均方误差分数。这对于调试训练函数或特定预测问题的特定配置非常有用。
您可以看到 random_codebook() 用于初始化码本向量,get_best_matching_unit() 函数用于查找每个训练模式在训练周期中的 BMU。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 训练一组码本向量 def train_codebooks(train, n_codebooks, lrate, epochs): codebooks = [random_codebook(train) for i in range(n_codebooks)] for epoch in range(epochs): rate = lrate * (1.0-(epoch/float(epochs))) sum_error = 0.0 for row in train: bmu = get_best_matching_unit(codebooks, row) for i in range(len(row)-1): error = row[i] - bmu[i] sum_error += error**2 if bmu[-1] == row[-1]: bmu[i] += rate * error else: bmu[i] -= rate * error print('>epoch=%d, lrate=%.3f, error=%.3f' % (epoch, rate, sum_error)) return codebooks |
我们可以将此与上面的示例结合起来,并为我们的人为构造数据集学习一组码本向量。
下面是完整的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
from math import sqrt from random import randrange from random import seed # 计算两个向量之间的欧几里得距离 def euclidean_distance(row1, row2): distance = 0.0 for i in range(len(row1)-1): distance += (row1[i] - row2[i])**2 return sqrt(distance) # 定位最佳匹配单元 def get_best_matching_unit(codebooks, test_row): distances = list() for codebook in codebooks: dist = euclidean_distance(codebook, test_row) distances.append((codebook, dist)) distances.sort(key=lambda tup: tup[1]) return distances[0][0] # 创建一个随机码本向量 def random_codebook(train): n_records = len(train) n_features = len(train[0]) codebook = [train[randrange(n_records)][i] for i in range(n_features)] return codebook # 训练一组码本向量 def train_codebooks(train, n_codebooks, lrate, epochs): codebooks = [random_codebook(train) for i in range(n_codebooks)] for epoch in range(epochs): rate = lrate * (1.0-(epoch/float(epochs))) sum_error = 0.0 for row in train: bmu = get_best_matching_unit(codebooks, row) for i in range(len(row)-1): error = row[i] - bmu[i] sum_error += error**2 if bmu[-1] == row[-1]: bmu[i] += rate * error else: bmu[i] -= rate * error print('>epoch=%d, lrate=%.3f, error=%.3f' % (epoch, rate, sum_error)) return codebooks # 测试训练函数 seed(1) dataset = [[2.7810836,2.550537003,0], [1.465489372,2.362125076,0], [3.396561688,4.400293529,0], [1.38807019,1.850220317,0], [3.06407232,3.005305973,0], [7.627531214,2.759262235,1], [5.332441248,2.088626775,1], [6.922596716,1.77106367,1], [8.675418651,-0.242068655,1], [7.673756466,3.508563011,1]] learn_rate = 0.3 n_epochs = 10 n_codebooks = 2 codebooks = train_codebooks(dataset, n_codebooks, learn_rate, n_epochs) print('Codebooks: %s' % codebooks) |
运行此示例将以 0.3 的初始学习率训练 10 个训练周期的 2 个码本向量。每隔训练周期都会打印详细信息,并显示从训练数据中学习到的 2 个码本向量集。
我们可以看到学习率的变化符合我们上面对每个训练周期的预期。我们还可以看到每个训练周期的均方误差在训练结束时确实在下降,并且可能有一个机会进一步调整示例以获得更少的误差。
1 2 3 4 5 6 7 8 9 10 11 |
>epoch=0, lrate=0.300, error=43.270 >epoch=1, lrate=0.270, error=30.403 >epoch=2, lrate=0.240, error=27.146 >epoch=3, lrate=0.210, error=26.301 >epoch=4, lrate=0.180, error=25.537 >epoch=5, lrate=0.150, error=24.789 >epoch=6, lrate=0.120, error=24.058 >epoch=7, lrate=0.090, error=23.346 >epoch=8, lrate=0.060, error=22.654 >epoch=9, lrate=0.030, error=21.982 Codebooks: [[2.432316086217663, 2.839821664184211, 0], [7.319592257892681, 1.97013382654341, 1]] |
既然我们知道如何训练一组码本向量,让我们来看看如何将此算法应用于真实数据集。
4. 电离层案例研究
在本节中,我们将把学习向量量化算法应用于电离层数据集。
第一步是加载数据集并将加载的数据转换为我们可以用于欧几里得距离计算的数字。为此,我们将使用辅助函数 load_csv() 加载文件,使用 str_column_to_float() 将字符串数字转换为浮点数,并使用 str_column_to_int() 将类别列转换为整数值。
我们将使用 k 折交叉验证(5 折)来评估该算法。这意味着每折将包含 351/5 = 70.2 或略多于 70 条记录。我们将使用辅助函数 evaluate_algorithm() 来通过交叉验证评估算法,并使用 accuracy_metric() 来计算预测的准确率。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# LVQ 用于电离层数据集 from random import seed from random import randrange from csv import reader from math import sqrt # 加载 CSV 文件 def load_csv(filename): dataset = list() with open(filename, 'r') as file: csv_reader = reader(file) for row in csv_reader: if not row: continue dataset.append(row) return dataset # 将字符串列转换为浮点数 def str_column_to_float(dataset, column): for row in dataset: row[column] = float(row[column].strip()) # 将字符串列转换为整数 def str_column_to_int(dataset, column): class_values = [row[column] for row in dataset] unique = set(class_values) lookup = dict() for i, value in enumerate(unique): lookup[value] = i for row in dataset: row[column] = lookup[row[column]] return lookup # 将数据集分成 k 折 def cross_validation_split(dataset, n_folds): dataset_split = list() dataset_copy = list(dataset) fold_size = int(len(dataset) / n_folds) for i in range(n_folds): fold = list() while len(fold) < fold_size: index = randrange(len(dataset_copy)) fold.append(dataset_copy.pop(index)) dataset_split.append(fold) return dataset_split # 计算准确率百分比 def accuracy_metric(actual, predicted): correct = 0 for i in range(len(actual)): if actual[i] == predicted[i] : correct += 1 return correct / float(len(actual)) * 100.0 # 使用交叉验证分割评估算法 def evaluate_algorithm(dataset, algorithm, n_folds, *args): folds = cross_validation_split(dataset, n_folds) scores = list() for fold in folds: train_set = list(folds) train_set.remove(fold) train_set = sum(train_set, []) test_set = list() for row in fold: row_copy = list(row) test_set.append(row_copy) row_copy[-1] = None predicted = algorithm(train_set, test_set, *args) actual = [row[-1] for row in fold] accuracy = accuracy_metric(actual, predicted) scores.append(accuracy) 返回 分数 # 计算两个向量之间的欧几里得距离 def euclidean_distance(row1, row2): distance = 0.0 for i in range(len(row1)-1): distance += (row1[i] - row2[i])**2 return sqrt(distance) # 定位最佳匹配单元 def get_best_matching_unit(codebooks, test_row): distances = list() for codebook in codebooks: dist = euclidean_distance(codebook, test_row) distances.append((codebook, dist)) distances.sort(key=lambda tup: tup[1]) return distances[0][0] # 使用码本向量进行预测 def predict(codebooks, test_row): bmu = get_best_matching_unit(codebooks, test_row) return bmu[-1] # 创建一个随机码本向量 def random_codebook(train): n_records = len(train) n_features = len(train[0]) codebook = [train[randrange(n_records)][i] for i in range(n_features)] return codebook # 训练一组码本向量 def train_codebooks(train, n_codebooks, lrate, epochs): codebooks = [random_codebook(train) for i in range(n_codebooks)] for epoch in range(epochs): rate = lrate * (1.0-(epoch/float(epochs))) for row in train: bmu = get_best_matching_unit(codebooks, row) for i in range(len(row)-1): error = row[i] - bmu[i] if bmu[-1] == row[-1]: bmu[i] += rate * error else: bmu[i] -= rate * error return codebooks # LVQ 算法 def learning_vector_quantization(train, test, n_codebooks, lrate, epochs): codebooks = train_codebooks(train, n_codebooks, lrate, epochs) predictions = list() for row in test: output = predict(codebooks, row) predictions.append(output) return(predictions) # 在 Ionosphere 数据集上测试 LVQ seed(1) # 加载并准备数据 filename = 'ionosphere.csv' dataset = load_csv(filename) for i in range(len(dataset[0])-1): str_column_to_float(dataset, i) # 将类别列转换为整数 str_column_to_int(dataset, len(dataset[0])-1) # 评估算法 n_folds = 5 learn_rate = 0.3 n_epochs = 50 n_codebooks = 20 scores = evaluate_algorithm(dataset, learning_vector_quantization, n_folds, n_codebooks, learn_rate, n_epochs) print('Scores: %s' % scores) print('Mean Accuracy: %.3f%%' % (sum(scores)/float(len(scores)))) |
运行此示例将打印每个折叠的分类准确率以及所有折叠的平均分类准确率。
我们可以看到 87.143% 的准确率优于 64.286% 的基线。我们还可以看到,我们 20 个码本向量的库远少于保留整个训练数据集。
1 2 |
Scores: [90.0, 88.57142857142857, 84.28571428571429, 87.14285714285714, 85.71428571428571] Mean Accuracy: 87.143% |
扩展
本节列出了你可能希望探索的教程扩展。
- 调整参数。上述示例中的参数尚未调整,请尝试不同的值以提高分类准确率。
- 不同的距离度量。尝试不同的距离度量,例如曼哈顿距离和闵可夫斯基距离。
- 多遍 LVQ。码本向量可以通过多次训练来更新。尝试使用大的学习率进行训练,然后使用较小的学习率进行大量轮次的训练,以微调码本。
- 更新更多 BMU。尝试在训练时选择多个 BMU,并将它们推离或拉近训练数据。
- 更多问题。将 LVQ 应用于 UCI 机器学习存储库上的更多分类问题。
您是否探索过这些扩展?
在下面的评论中分享您的经验。
回顾
在本教程中,您将学习如何从头开始在 Python 中实现学习向量量化算法。
具体来说,你学到了:
- 如何计算模式之间的距离并定位最佳匹配单元。
- 如何训练一组码本向量以最好地总结训练数据集。
- 如何将学习向量量化算法应用于实际的预测建模问题。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
嗨,Brownlee先生,
我已经将 LVQ 应用于我的数据集,它给了我 57.546% 的平均准确率。您能否建议一下导致准确率如此之低的原因?
是的,这里有一些提高性能的通用建议
https://machinelearning.org.cn/machine-learning-performance-improvement-cheat-sheet/
这里有一些关于 LVQ 的具体建议(在算法用法说明和启发式方法下)
http://wekaclassalgos.sourceforge.net/
另请参阅
http://cleveralgorithms.com/nature-inspired/neural/lvq.html
希望这能作为起点。关于此算法的最佳来源是 Kohonen 的书。
你好 Glen,晚上好,我想将 PSO + LVQ 应用于我的数据集。你能否帮助我在 R 中编码?
您好,如何为 mfcc 值实现码本形成,请回答。
什么是“mfcc 值的码本形成”?
我尝试在以下数据集的电力数据上运行它:http://www.inescporto.pt/~jgama/ales/ales_5.html
我在 arff 文件上使用了 LVQ,但算法的性能非常差。准确率约为 60%。你能尝试在此数据集上使用该算法并告诉我你的结果或改进方法吗?
这里有一些可以尝试改进性能的想法
https://machinelearning.org.cn/machine-learning-performance-improvement-cheat-sheet/
嗨,我如何使用 LVQ 进行实例选择?比如,我想从数据集中选择 5000 个实例,而不是 15000 个实例?请给出一些细节。
抱歉,我不太明白你的问题,也许你能解释一下你的意图或目标?
我想知道我们是否可以使用 LVQ 进行实例选择。例如,如果我的数据集中有 15000 个实例,而我只想使用其中的 5000 个实例作为训练数据集。有各种“实例选择”算法,我读到我们也可以使用 LVQ。但是我不确定如何使用你上面的例子来做到这一点。
我明白了,你的意思是将 LVQ 用作数据预处理或数据清理方法。
也许吧。我没做过,但我喜欢这个主意。比如说,使用一个阈值,只选择距离码本向量在某个范围内的实例。
尝试一下,看看效果——你可能会发现一些非常有趣的东西。如果你有结果,请分享!
如果我说错了请纠正我。
我从好奇的孩子的提问中理解的是
假设我们有一个包含 15000 个实例的数据集……
例子
0.000133,5,0.446809,0.089798,0.516662,0.003467,0.422915,0.414912,UP
0.000133,5,0.468085,0.047676,0.50476,0.003467,0.422915,0.414912,DOWN
0.000133,5,0.489362,0.146902,0.495537,0.003467,0.422915,0.414912,UP
0.000133,5,0.510638,0.193287,0.485272,0.003467,0.422915,0.414912,UP
.
.
.
……
现在他想使用 LVQ 进行实例选择(数据预处理)。这意味着最终结果应该包含 5000 个最佳实例,他之后可以使用这些实例进行分类。
现在,正如你所建议的,可以通过设置一个阈值,只选择距离码本向量在某个范围内的实例来实现。
这样做意味着我们根据与随机生成的码本的距离来选择数据集中的实例。例如,在“get_best_matching_unit”函数中,如果我们使用这个距离“distances.append((codebook, dist))”(第 86 行),那么我们就不会像你在第 109-114 行代码中那样训练码本,因此 LVQ 的主要功能将不会被实现(即,在轮次中训练随机码本并根据误差调整 bmu)。如果我们训练了码本,那么我们将丢失距离值,因为你在第 88 行返回的是“distances[0][0]”。
我的想法是通过训练你代码中实现的码本并获得第 115 行中的最终码本值来实现。将 n_codebooks 的值设为 5000,这将为我们提供代表数据的最佳 5000 个实例(因为一个码本代表一个实例)。然后使用这 5000 个实例进行分类(使用任何分类算法)。
你对这个方法有什么建议吗?我很乐意知道。
有趣。这样使用 LVQ 是一种异常值去除。
在低维度(数据没有异常值)时,它可能相当有效,但在高维度时,由于维度灾难,距离和阈值很快变得没有意义,因此效果会变差。通常,LVQ 和 kNN 出于同样的原因在高维度下会失效
https://en.wikipedia.org/wiki/Curse_of_dimensionality
是的,码本向量可以被视为质心或数据的投影。我将这种方法与 PCA、Sammons Mapping、tsne 等投影方法进行对比。从每个投影开发模型,并在保留的数据集上比较模型性能。
我很想听听结果。
我正在考虑使用码本向量和新数据之间的欧几里得距离,然后计算这些距离的平均值作为阈值。然后我计划选择所有欧几里得距离小于计算出的平均值的实例。你觉得这样好吗,还是我需要换个思路?
试一试。
在二维空间中使用人为构造的数据进行一些模拟可能会很有价值。
请记住,特征越多——维度越高,维度灾难会使距离越来越没有意义。
我无法理解“learning_vector_quantization”函数的功能。例如,它在代码的哪里被调用?因此,“predict”函数将不会被调用。我通过注释掉 predict 函数得到了相同的结果。如果我错了或者遗漏了什么,请纠正我。
您好 Alex,
“learning_vector_quantization”函数作为参数传递给“evaluate_algorithm”函数,在最终代码列表的第 68 行被调用。
这样有帮助吗?尝试添加一些 print() 调试语句以帮助您进一步澄清。
谢谢 Jason。我现在明白了。我还有另一个疑问。你在博客中提到,一旦码本向量准备好,这些码本向量就会被用来通过 1-NN 进行预测。但我无法理解你在代码中是如何做到的。请详细说明一下。
你哪里不明白,Alex?
具体的代码行?
kNN 算法?
如何将其应用于大型图像数据库?我有一个文件夹里装满了图像,如何将它们转换为数组以便对其应用 lqv?
抱歉 Navin,我没有处理原始图像和 LVQ 的示例。
你能给我一个关于如何使用你的实现来处理原始图像的想法吗?
你能分享一下 Kohonen 自组织映射方法吗?
我真的很想知道这种方法中的无监督学习是如何工作的。
谢谢,抱歉我的英语不好。
是的,请看这里
http://cleveralgorithms.com/nature-inspired/neural/som.html
嗨,Jason,
我创建了一个带有训练数据和两个样本的 LVQ 网络。它有效,但如何为未训练的数据获取置信度值?例如,置信度为 0.98 意味着样本非常接近其中一个样本的实际模型。
好问题,我不知道。凭直觉,我认为你可以根据距离度量和域的已知范围来设计置信区间。
感谢您的回复。由于我将所有值都归一化到 0 到 1 之间,那么我可以使用什么作为最大值?我尝试使用 1 - distance(bestUnitMatch, sample),我得到的值是 0.96;如果这是正确的,我感到非常惊讶,因为我只用每个集合的 30 个样本训练了网络。
也许是你的域中任意两点之间的最大距离?
你好,我正在学习 LVQ,这是我学习的一部分,你能把完整的代码发给我吗?这样我能更好地学习它。用于教育目的。谢谢
完整的代码在上面的帖子中提供。
你好,
在“train_codebooks()”函数中,“codebooks”变量在哪里被更新?只有“bmu”变量被移向训练向量或移离训练向量,而这并没有用于更新“codebooks”向量。
如果我遗漏了什么,请告诉我。
谢谢你
正确,每次训练模式只更新 BMU。
我认为应该将这个更新后的 BMU 保存回码本,以确保学习正在进行。
不需要,我们有它的引用副本。
也许可以了解一下现代编程语言中的“按引用传递”?
谢谢您的澄清。
我不知道“按引用传递”在 Python 中是如何工作的。
没关系,它与其他大多数语言一样。
你好,谢谢你提供如此精彩的演示。我不是 Python pro,但有一些问题。
在第 61 行,train_set.remove(fold) 会返回一个奇怪的错误:“ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()”。
有什么替代方法吗?
另外,我想知道为什么使用列表而不是 numpy 数组或数据帧。有特别的原因吗?
再次感谢。
我使用 Python 2.7 编写代码,也许请检查一下你的 Python 版本?
运行代码时出现这个错误?我该怎么办?
Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 17:26:49) [MSC v.1900 32 bit (Intel)] on win32
输入“copyright”、“credits”或“license()”获取更多信息。
>>>
==== RESTART: H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py ====
回溯(最近一次调用)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 140, in
scores = evaluate_algorithm(dataset, learning_vector_quantization, n_folds, n_codebooks, learn_rate, n_epochs)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 68, in evaluate_algorithm
predicted = algorithm(train_set, test_set, *args)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 119, in learning_vector_quantization
codebooks = train_codebooks(train, n_codebooks, lrate, epochs)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 110, in train_codebooks
error = row[i] – bmu[i]
IndexError: 列表索引超出范围
>>>
代码假定您使用的是 Python 2 版本,而您使用的是 Python 3。
您可以更改您的 Python 版本或更改代码。
我也遇到了这个错误!尽管我使用的是 python 2.7
Python 2.7.14 (v2.7.14:84471935ed, 2017 年 9 月 16 日,20:19:30) [MSC v.1500 32 位 (Intel)] on win32
输入“copyright”、“credits”或“license()”获取更多信息。
>>>
==== RESTART: H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py ====
回溯(最近一次调用)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 140, in
scores = evaluate_algorithm(dataset, learning_vector_quantization, n_folds, n_codebooks, learn_rate, n_epochs)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 68, in evaluate_algorithm
predicted = algorithm(train_set, test_set, *args)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 119, in learning_vector_quantization
codebooks = train_codebooks(train, n_codebooks, lrate, epochs)
File “H:/Ebooks/Ebooks/machine/project/New folder (3)/LVQ/lvq.py”, line 110, in train_codebooks
error = row[i] – bmu[i]
IndexError: 列表索引超出范围
>>>
请尝试这些步骤,然后告诉我进展如何。
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
我无法在没有空格的情况下使用代码,也无法猜测它,因为我不是 Python 专家,请帮助我!
本示例将向您展示如何从教程中复制粘贴代码。
https://machinelearning.org.cn/faq/single-faq/how-do-i-copy-code-from-a-tutorial
我收到此错误
error = row[i] – bmu[i]
IndexError: 列表索引超出范围
您使用的是 Python 2.7 吗?
亲爱的Jason
我是 Mahendhiran。我正在尝试使用音频数据(MFCC)执行 LVQ。我遇到了类似 ‘row[column] = float(row[column].strip())’ 的错误。
TypeError: ‘int’ object is not subscriptable’
我请求您帮助解决此问题。我正在使用 spyder(python 3.6)。
提前感谢…。
该代码是为 Python 2.7 编写的。
你好,
您的初始代码向量集不应包含每个类别值的代表吗?
否则,您的最后一步,即 1-最近邻算法,将始终返回相同的类别值,对吗?
谢谢你
是的,说得非常好。
嗨,Jason Brownlee,
您的 LVQ 代码是否可用于包含高达 100,000 行和 28 个特征的大型数据集?另外,为什么您的代码对于包含特征名称的数据集不起作用?
可能可以。该代码仅用于教育目的——教授该方法的工作原理。
您可能需要考虑使用高效的库实现或使用 numpy 数组。
训练后如何保存 LVQ 模型来分类未知数据?
您可以将代码向量保存到文件,格式为文本或 numpy 数组,之后再加载它们。
predict(codebooks, row) 函数可以随时使用数据行进行调用。
你好,
运行此代码时,我收到以下错误。您能告诉我如何解决吗?
ValueError 回溯 (最近一次调用)
in
130 dataset = load_csv(filename)
131 for i in range(len(dataset[0])-1)
–> 131 str_column_to_float(dataset, i)
133 # convert class column to integers
134 str_column_to_int(dataset, len(dataset[0])-1)
在 str_column_to_float(dataset, column)中
19 def str_column_to_float(dataset, column)
20 for row in dataset
—> 21 row[column] = float(row[column])
22
23 # Convert string column to integer
ValueError: 无法将字符串转换为浮点数
很抱歉听到这个消息,我在这里有一些建议。
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
Jason,您是否有关于使用 LVQ 实现 SOM(监督学习)的教程?
我确实有一个较旧的教程可能有所帮助。
http://cleveralgorithms.com/nature-inspired/neural/som.html
感谢您精彩的教程。我如何改进代码以实现 LVQ2?
好问题,也许这会给您一些启发。
http://cleveralgorithms.com/nature-inspired/neural/lvq.html
该页面未找到,为什么?
您好,我将您的 LVQ 用于 JAFFE 数据集,并且我还使用了 PCA 进行特征提取。但我得到的准确率非常低。也许您对这个准确率问题有什么建议?
谢谢。
或许可以尝试这里的一些建议
https://machinelearning.org.cn/machine-learning-performance-improvement-cheat-sheet/
嗨,Jason,
我是一名学生,刚接触机器学习,我觉得这个 LVQ 的解释很有趣。您能告诉我“new test_row”是什么意思吗?我明白“codebook vectors”是指原始数据集,但我不明白“new test_row”。我正在使用 Iris 数据集来实现它。
谢谢。
new test row 是一个在训练期间未见过的、我们希望对其进行预测的新样本。
您好 Jason,我尝试了您在此链接中发布的代码,但在更新 BMU(在我的用例中使用 6 个原型)时卡住了。您有什么建议吗?
您将只有一个或两个 BMU。或许可以将代码按原样用作起点。
我正在寻找积量化算法。
那具体是什么?
用于大规模近似最近邻搜索的积量化示例代码。
抱歉,我不知道那是什么。
亲爱的 Jason,
我想问您,您是否知道如何学习标量量化对于 n 维向量的情况,例如均匀量化?
什么是“标量量化”?
您好 Jason,感谢您的工作!
我是机器学习新手,这段代码是否包含像 sklearn 那样的训练/测试拆分?在我的数据集中,我在数组末尾放置了一个标签,如何将标签提取出来,以便标签成为输出?
谢谢!
标签不作为输入使用,请记住机器学习模型接受输入并预测输出。将输出作为输入是一个错误。
那么,例如我想用 LVQ 预测性别,系统是如何决定说话者是男性还是女性的?输出结果是预测,例如“您是男性”。
非常感谢
它将根据您提供给模型的任何输入数据进行学习,以便做出预测。
您好 Jason,您有什么关于在不使用任何科学库的情况下可视化 LVQ 模型输出的建议吗?
并不是真的。
也许可以通过按类别值着色的代码向量的 PCA 投影?
您是否有关于“按类别值着色的代码向量的 PCA 投影”的参考资料,以便我可以自己尝试?
抱歉,我没有。这会帮助您开始。
https://machinelearning.org.cn/calculate-principal-component-analysis-scratch-python/
您好 Jason,我对“def train_codebooks”感到有些困惑,特别是为什么我要在 bmu 和 row 的最后一个元素中进行检查,以确定是添加还是减去 lr*error,即:
for i in range(len(row)-1)
error = row[i] – bmu[i]
if bmu[-1] == row[-1]
bmu[i] += rate * error
else
bmu[i] -= rate * error
最后一个元素是类别标签。
LVQ 是一种启发式算法。LVQ 有一个变体称为 GLVQ (https://papers.nips.cc/paper/1113-generalized-learning-vector-quantization.pdf),它具有可微分的成本函数,可以使用梯度下降进行训练。您是否有在 Python 中实现它的教程?
我没有。
您能否使用 scikit 和 matplotlib 实现一个 LVQ 模型,只需在任何数据集上进行非常简单的训练/测试和准确性评估?
我认为 sklearn 没有 LVQ 实现。
您好,感谢您的教程和您博客的质量。
为什么不直接计算每个总体的质心,然后应用 1-最近邻测试?
此致,
不客气。
什么是“质心”?
当然,您可以开发任何您喜欢的模型。本教程是关于一个特定的著名模型。
代码向量的随机化方式,使得某些类别可能在初始代码向量中没有代表。因此,我们需要随机化以包含每个类别的代表。
可能,但由于随机化的结果,我们认为其概率很低。
你好。
如何移除不再赢得战斗的权重向量是最好的方法?
您的评论正在等待审核。
Hi MiGoreng…推荐的方法是减少不被认为重要的权重的效果。
https://machinelearning.org.cn/weight-regularization-to-reduce-overfitting-of-deep-learning-models/
您好,关于测试结果的快速澄清。
如果我使用了您的数据集,结果是好的,平均准确率很高(87.353%),而如果我使用了我自己提供的数据集,结果是不可靠的。
我尝试更改有关 epoch 数量、fold 数量的参数,但没有效果。结果很糟糕。
可能的原因是什么?您能帮我找到解决方案吗?
谢谢
Alex
Hi Alex…请澄清您用于生成数据的所用方法,以便我们能更好地帮助您。
您好 James,感谢您的支持。
我准备了一个关于工业过程的包含 340 列和 65000 行的数据集。
我从头开始复制了您的所有代码,然后选择了我的数据集,指明了数据集的名称并运行了代码。
运行过程结束后,响应为零。
分数:[0.0, 0.0, 0.0,…… 0.0]
平均准确率:0.0%
因此,通过这种方式,我尝试修改 epoch 数量、fold 数量等参数,但都没有成功。
目前,我不知道如何解决这个错误。