18-葡萄酒的分类
7.5.3 葡萄酒的分类
下面将用另一个数据集对本章的神经网络模型进行测试,该数据集是基于对多个意大利葡萄酒品种的化学分析得来的[4]。数据集中有178个样本。使用方式与鸢尾花数据集大致相同,只是CSV文件的布局稍有差别。下面给出一个示例:
1,14.23,1.71,2.43,15.6,127,2.8,3.06,.28,2.29,5.64,1.04,3.92,1065
1,13.2,1.78,2.14,11.2,100,2.65,2.76,.26,1.28,4.38,1.05,3.4,1050
1,13.16,2.36,2.67,18.6,101,2.8,3.24,.3,2.81,5.68,1.03,3.17,1185
1,14.37,1.95,2.5,16.8,113,3.85,3.49,.24,2.18,7.8,.86,3.45,1480
1,13.24,2.59,2.87,21,118,2.8,2.69,.39,1.82,4.32,1.04,2.93,735
每行的第一个值一定是1到3之间的整数,代表该条样本为3个品种之一。但请注意这里用于分类的参数更多一些。在鸢尾花数据集中,只有4个参数。而在这个葡萄酒数据集中,则有13个参数。
本章的神经网络模型的扩展性非常好,这里只需增加输入神经元的数量即可。wine_test.py类似于iris_test.py,但为了适应数据文件的布局差异而进行了一些微小的改动。具体代码如代码清单7-19所示。
代码清单7-19 wine _test.py
import csv
from typing import List
from util import normalize_by_feature_scaling
from network import Network
from random import shuffle
if __name__ == "__main__":
wine_parameters: List[List[float]] = []
wine_classifications: List[List[float]] = []
wine_species: List[int] = []
with open('wine.csv', mode='r') as wine_file:
wines: List = list(csv.reader(wine_file, quoting=csv.QUOTE_NONNUMERIC))
shuffle(wines) # get our lines of data in random order
for wine in wines:
parameters: List[float] = [float(n) for n in wine[1:14]]
wine_parameters.append(parameters)
species: int = int(wine[0])
if species == 1:
wine_classifications.append([1.0, 0.0, 0.0])
elif species == 2:
wine_classifications.append([0.0, 1.0, 0.0])
else:
wine_classifications.append([0.0, 0.0, 1.0])
wine_species.append(species)
normalize_by_feature_scaling(wine_parameters)
如前所述,在这个葡萄酒分类的神经网络模型中,层的参数需要用到13个输入神经元(每个参数一个神经元)。此外还需要3个输出神经元。(葡萄酒品种有3种,就像有3种鸢尾花一样。)有意思的是,虽然隐藏层中神经元的数量少于输入层中神经元的数量,但该神经网络对象的运行效果还算不错。一种直观的解释可能是,某些输入参数其实对分类没有帮助,在处理过程中将它们剔除会很有意义。当然,事实上这并不是隐藏层中神经元的数量减少却仍能正常工作的原因,但这种直观的想法还是挺有趣的。具体代码如代码清单7-20所示。
代码清单7-20 wine _test.py(续)
wine_network: Network = Network([13, 7, 3], 0.9)
与之前一样,不妨试验一下不同数量的隐藏层神经元或不同的学习率,这会很有趣的。具体代码如代码清单7-21所示。
代码清单7-21 wine _test.py(续)
def wine_interpret_output(output: List[float]) -> int:
if max(output) == output[0]:
return 1
elif max(output) == output[1]:
return 2
else:
return 3
wine_interpret_output() 类似于 iris_interpret_output() 。因为没有葡萄酒品种的名称,所以这里只能采用原数据集给出的整数值。具体代码如代码清单7-22所示。
代码清单7-22 wine _test.py(续)
# train over the first 150 wines 10 times
wine_trainers: List[List[float]] = wine_parameters[0:150]
wine_trainers_corrects: List[List[float]] = wine_classifications[0:150]
for _ in range(10):
wine_network.train(wine_trainers, wine_trainers_corrects)
数据集中的前150个样本将用于训练,剩下最后28个样本将用于验证。样本的训练次数为10次,明显少于训练鸢尾花数据集的50次。不知出于何种原因(可能是数据集的固有特性,也可能是学习率和隐藏神经元数量这些参数有调整),该数据集只需要少于鸢尾花数据集的训练次数就能达到高于鸢尾花数据集的准确度。具体代码如代码清单7-23所示。
代码清单7-23 wine _test.py(续)
# test over the last 28 of the wines in the data set
wine_testers: List[List[float]] = wine_parameters[150:178]
wine_testers_corrects: List[int] = wine_species[150:178]
wine_results = wine_network.validate(wine_testers, wine_testers_corrects, wine_
interpret_output)
print(f"{wine_results[0]} correct of {wine_results[1]} = {wine_results[2] * 100}%")
运气不错,这个神经网络模型应该能很准确地对28个样本进行分类。
27 correct of 28 = 96.42857142857143%