Skip to content

模型:特征与权重的数据库

模型的概念

什么是模型

我们经常能听到行业内的人提起模型,并且模型也是测试人员主要的测试对象。 那么模型究竟是什么呢。 简单来说我们可以把模型看做是一个数据库, 它负责存储特征以及对应的权重。

回顾一下之前的内容,我们知道机器学习其实简单来说就是无数的if else的组合,是一个极其庞大的专家系统。 这个系统里包括了非常多的规则来帮助用户来完成业务目标。 而上一节我们也讲到了特征是如何进行抽取的,并且强调了特征是人工智能中组重要的组成部分。 所以模型要负责存储这些特征以及对应的权重,在使用这个模型的时候 ,它的行为已点类似下面的伪代码:

识别结果 = 特征1*权重 + 特征2*权重 + 特征3*权重 +特征4*权重 。。。。。。+ 特征N*权重

实际上在业务中,当用户数据被传输到人工智能系统中,程序会依照预先设定好的规则提取特征。然后到模型这个特征数据库中查询是否存在这条特征并取出这条特征的权重。

比如一条用户数据到来后,系统根据职业这个字段提取出了程序员这个特征。 然后就会到模型中查询程序员这个特征是否存在并且取出它对应的权重。类似这样的,系统会在用户数据中依次提取特征并到模型中进行查询。这样上面的伪代码就成了:

识别结果 = 1(特征值)*0.1(权重值))+ 2(特征值)*0.2(权重值))......

假如上面的伪代码的计算结果为0.8。那么这个值就是模型最终的计算结果了。 如果放在信用卡反欺诈场景中,这个识别结果代表了当前的交易记录是信用卡盗刷行为的概率是80%。是的,在信用卡反欺诈中人工智能模型所要计算的,正是用户行为属于盗刷行为的概率是多少。

# 将所有特征组合成一个特征向量
vectorAssembler = VectorAssembler(inputCols=["age", "title_onehot", "price"], outputCol="feature")
data_vector = vectorAssembler.transform(data_encoded)

# 将数据划分为训练集和验证集,一部分数据用来训练,而一部分数据用来测试模型的效果
(train_data, test_data) = data_vector.randomSplit([0.8, 0.2], seed=1234)

from pyspark.ml.classification import LogisticRegression

# 创建Logistic回归模型
lr = LogisticRegression(labelCol="label", featuresCol="feature")

# 定义参数网格
paramGrid = ParamGridBuilder().addGrid(lr.regParam, [0.01, 0.1, 1]).addGrid(lr.elasticNetParam, [0.0, 0.5, 1.0]).build()

# 定义评估指标
evaluator = BinaryClassificationEvaluator(labelCol="label", metricName="areaUnderROC")

# 使用训练集和测试集进行模型调参和交叉验证
tvs = TrainValidationSplit(estimator=lr, estimatorParamMaps=paramGrid, evaluator=evaluator, trainRatio=0.8)
tvsModel = tvs.fit(train_data)
tvsModel.write().overwrite().save('./model')

上面的代码中我们通过spark ml 初始化了一个逻辑回归算法(机器学习算法中的一种),经过模型训练后,我们可以把模型保存到本地的model目录下

我们通过spark的API读取保存下来的模型summary,看看里面都保存了什么信息。

from pyspark import SparkContext, SparkConf, SQLContext
from pyspark.sql import Row

conf = SparkConf().setMaster("local").setAppName("My App")
sc = SparkContext(conf=conf)
sqlContext = SQLContext(sc)
dataA = sqlContext.read.parquet("/Users/frank/tools/workspace/spark-test/model/bestModel/data/part-00000-7c6713be-1fc8-41d8-b91f-6d448d21bad8-c000.snappy.parquet")

dataA.show(truncate=False)


+----------+-----------+---------------------+---------------------------------------------------------------+-------------+
|numClasses|numFeatures|interceptVector      |coefficientMatrix                                              |isMultinomial|
+----------+-----------+---------------------+---------------------------------------------------------------+-------------+
|2         |3          |[-22.560434545902464]|0.23497251841737565  -2.349725396006688  0.006726081325784561  |false        |
+----------+-----------+---------------------+---------------------------------------------------------------+-------------+

在运行结果中,我们看以下几个信息:

  • numClasses: 代表分类数量,我们训练的是一个二分类模型,所以这里的数字是2。
  • numFeatures:代表特征数量, 从上面的代码可以知道,我们提取了3个特征。
  • coefficientMatrix:系数矩阵,系数矩阵是一个包含每个特征与目标变量之间关系的矩阵。
  • interceptVector:截距向量,截距向量是一个包含每个目标变量的截距值的向量。

当模型被保存到本地后, 我们也可以随时通过代码去读取模型并用于模型推理(预测)。

# 将所有特征组合成一个特征向量
vectorAssembler = VectorAssembler(inputCols=["age", "title_onehot", "price"], outputCol="feature")
data_vector = vectorAssembler.transform(data_encoded)

# 从模型文件中加载模型,
model = TrainValidationSplitModel.load('../model')
predictions = model.transform(data_vector)
predictions.show()



+---------------+------+---+--------+-----+-----+---------+-------------+-----------------+--------------------+--------------------+----------+
|           name|gender|age|   title|price|label|title_num| title_onehot|          feature|       rawPrediction|         probability|prediction|
+---------------+------+---+--------+-----+-----+---------+-------------+-----------------+--------------------+--------------------+----------+
|          frank|    男| 16|  程序员| 3600|  1.0|      0.0|(1,[0],[1.0])|[16.0,1.0,3600.0]|[-3.0632931255932...|[0.04464702833406...|       1.0|
|           alex|    女| 26|项目经理| 3000|  1.0|      1.0|    (1,[],[])|[26.0,0.0,3000.0]|[-3.7270949103029...|[0.02349723309320...|       1.0|
|          frank|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|           asdf|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|       fragfsnk|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|      frasdfgnk|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|      frsdfgank|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|    frsdfgdfank|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|frsdfgdfankdsaf|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
| frsdfgdfank342|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
| frsdfgdfank445|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
| frsdfgdfank756|    男| 16|  程序员| 3600|  1.0|      0.0|(1,[0],[1.0])|[16.0,1.0,3600.0]|[-3.0632931255932...|[0.04464702833406...|       1.0|
|           hdfg|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
| frsdfncvgdfank|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|           wert|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|           sdfg|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|  frssdffgdfank|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|           asdf|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|           zxcv|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
|    frsdfgdfank|    男| 16|  程序员| 2600|  0.0|      0.0|(1,[0],[1.0])|[16.0,1.0,2600.0]|[3.66278820019128...|[0.97498114032739...|       0.0|
+---------------+------+---+--------+-----+-----+---------+-------------+-----------------+--------------------+--------------------+----------+

上面代码中, 我们通过model.transform把数据输入给模型后,返回的predictions就是模型的推理结果了。