一旦我们有了全部的特征,接下来就可以输出整个训练集(评判附加特征)到一��新的文件(sample_judgements_wfeatures.txt):
buildFeaturesJudgmentsFile(judgements, filename='sample_judgements_wfeatures.txt')相应地将输出一个完整的具体RankLib评分列表:
3 qid:1 1:9.476478 2:25.821222 # 1370 3 qid:1 1:6.822593 2:23.463709 # 1369这里特征1是在属性title(1.json.jinja)上检索“Rambo”时的TF*IDF分值;特征2是更复杂的检索(2.json.jinja)的TF*IDF分值。
接下来进行训练!这一行是通过命令行使用已保存文件作为评判数据来执行Ranklib.jar
trainModel(judgmentsWithFeaturesFile=' sample_judgements_wfeatures.txt', modelOutput='model.txt')正如下文所示,这只是很基础地执行Java -jar Ranklib.jar来训练一个LambdaMART模型:
def trainModel(judgmentsWithFeaturesFile, modelOutput): # java -jar RankLib-2.6.jar -ranker 6 -train sample_judgements_wfeatures.txt -save model.txt cmd = "java -jar RankLib-2.6.jar -ranker 6 -train %s -save %s" % (judgmentsWithFeaturesFile, modelOutput) print("Running %s" % cmd) os.system(cmd)然后使用简单的Elasticsearch命令把模型保存到Elasticsearch中:
saveModel(es, scriptName='test', modelFname='model.txt')这里的savaModel跟看起来一样,只是读取文件内容并POST到Elasticsearch中,作为一个ranklib脚本存储。
使用LTR模型进行搜索一旦完成训练,就准备好了发起检索!在search.py中有一个非常简明直观的例子,里面只有一个简单查询。执行命令python search.py rambo,将会使用训练好的模型检索“rambo”,并执行以下重打分查询:
{ "query": { "match": { "_all": "rambo" } }, "rescore": { "window_size": 20, "query": { "rescore_query": { "ltr": { "model": { "stored": "test" }, "features": [{ "match": { "title": "rambo" } }, { "multi_match": { "query": "rambo", "type": "cross_fields", "tie_breaker": 1.0, "fields": ["overview", "genres.name", "title", "tagline", "belongs_to_collection.name", "cast.name", "directors.name"] } }] } } } } }注意这里我们只对前20个结果做重排序。也可以直接使用LTR查询,实际上直接运行模型更好一些,即使在整个集合上运行要耗费几百毫秒。对于一个较大的集合可能并不可行。一般来讲,最好只对前面N个结果做重排序,因为机器学习排序模型的性能成本的原因。
这就是一个刚好能工作的完整例子了。当然只是一个入门级的小规模示例,刚刚能达到目的;对于特定问题可能会有更多不同的状况,所选的特征、如何记录特征、训练模型,以及实现一个排序基线函数,基本上依赖于你的领域。但我们在《相关性检索》中提到的很多内容仍然适用。
后续内容后续的博客文章中我们会更多地介绍LTR,包括:
基础:更多地介绍LTR到底是什么
应用:将LTR应用于搜索、推荐系统、个性化及更多场景
模型:流行的模型是什么?模型选择时如何考量?
思考:使用LTR时有哪些技术和非技术因素需要考量?