根据职位说明使用机器学习来检索相关简历
我们使用平均词嵌入(AWE)模型基于职业描述来检索相关的CV。我们在这提供了一个循序渐进的指南,通过使用西班牙语的文件(简历)训练,将已训练的领域词嵌入与预先训练好嵌入结合起来。我们还使用主要成分分析(PCA)作为一种缩减技术,用于将类似的维度用于单词嵌入结果。
架构描述
信息检索(IR)模型是由一个索引语料库和评分或排序功能所组成的。IR系统的主要目标是根据用户请求检索相关文档或网页。在检索过程中,评分功能根据检索到的文档与用户查询的相关性来对检索到的文档进行排序。诸如像BM25和语言模型这样的经典IR模型都是基于bag-of-words(BOW)索引方案。BOW模型有两个主要弱点:它们丢失了出现单词的上下文,而且也忽略了它的语义。潜在语义索引Latent semantic indexing(LSI)是一种用于处理这个问题的技术,但是当文档数量增加时,索引处理计算量将变得很大。最标准的解决这个问题的方法就是训练单词或语句嵌入到语料库中或者使用预训练的语料库。
字嵌入(WE)是从神经网络模型获得的术语的分布式表示。这些连续的表示近期已经被用于不同的自然语言处理任务中。平均词嵌入(AWE)是一种比较受欢迎的表示长文本序列的技术,它不仅仅是表示一个术语。
在我们的例子中,我们需要一组简历,但由于工作的描述是事先未知的,我们需要提供一个基于无监督学习方法的解决方案。因此,词嵌入的使用似乎是我们实验的一个很好的起点。
下图是架构的定义。
步骤1:训练域词嵌入(已训WEs)
作为第一步,我们从四个已知的职业(Java工程师,测试工程师Tester,人力资本管理SAP HCM和销售与分销SAP SD)中构建一个平均的简历文档,使得四个职业简历文档数量相当。由于简历是用不同格式和不同风格和词汇编写的,所以我们决定只使用名词和动词,以便从简历中获得重要和相关的信息。建立语料库后,我们将他传输给Word2vec,并设定以下参数:窗口大小为5,最小字数为3,维数为200. CBOW默认使用的就是Word2vec模型。
我们在Linux Ubuntu 16.04 LTS上使用Python 3.6.1和64位的Anaconda 。为了安装多个库,pip
必须按如下所示运行install命令:
pip install gensim
pip install pattern3
pip install textract
pip install numpy
pip install scipy
pip install sklearn
pip install pickle
在安装了所有需要的软件包之后,我们创建一个函数来从特定的文件夹中检索所有的CV,读取它们(使用textract),将它们解析(使用模式3),最后创建嵌入字(使用gensim)。负责从简历(PDF,TXT,DOC,DOCX)中提取文本的python函数定义如下:
from gensim.models import Word2Vec, KeyedVectors
from pattern3 import es
import textract
from os import listdir
rom os.path import isfile, join
import numpy as np
from scipy import spatial
from sklearn import decomposition
import matplotlib.pyplot as plt
import pickle
def read_All_CV(filename):
text = textract.process(filename)
return text.decode('utf-8')
#Next, we define a function to parse the documents (CVs) and save the word embeddings as follows:
def preprocess_training_data1(dir_cvs, dir_model_name):
dircvs = [join(dir_cvs, f) for f in listdir(dir_cvs) if isfile(join(dir_cvs, f))]
alltext = ' '
for cv in dircvs:
yd = read_All_CV(cv)
alltext += yd + " "
alltext = alltext.lower()
vector = []
for sentence in es.parsetree(alltext, tokenize=True, lemmata=True, tags=True):
temp = []
for chunk in sentence.chunks:
for word in chunk.words:
if word.tag == 'NN' or word.tag == 'VB':
temp.append(word.lemma)
vector.append(temp)
global model
model = Word2Vec(vector, size=200, window=5, min_count=3, workers=4)
model.save(dir_model_name)
一旦所有的嵌入被保存进 dir_model_name
,我们已经完成了将单词嵌入设置到全局变量模型的任务,我们可以使用PCA技术来减少预训练词嵌入的维度。
第2步:下载并减少预训练字嵌入(Pretrained PCA WEs)
在我们下载西班牙预训练词并嵌入后,我们观察到这些向量共有300个维度,我们提出的领域训练的嵌入有200个维度。于是我们决定将300维矢量缩减为200维,然后用两个词嵌入空间来构建混合空间。以下函数负责减少预先训练的词嵌入的维度:
def reduce_dimensions_WE(dir_we_SWE, dir_pca_we_SWE):
m1 = KeyedVectors.load_word2vec_format(dir_we_SWE ,binary=True)
model1 = {}
# normalize vectors
for string in m1.wv.vocab:
model1[string]=m1.wv[string] / np.linalg.norm(m1.wv[string])
# reduce dimensionality
pca = decomposition.PCA(n_components=200)
pca.fit(np.array(list(model1.values())))
model1=pca.transform(np.array(list(model1.values())))
i = 0
for key, value in model1.items():
model1[key] = model1[i] / np.linalg.norm(model1[i])
i = i + 1
with open(dir_pca_we_SWE, 'wb') as handle:
pickle.dump(model1, handle, protocol=pickle.HIGHEST_PROTOCOL)
return model1
一旦获得了简化矢量,我们可以使用混合词嵌入和AWE来执行检索任务,以计算简历(CV)和查询(职业描述)的均值向量。
步骤3:构建混合词嵌入空间并检索相关简历(CV)
我们展示了一个在实验室中开发的服务,我们实际上加载了两个嵌入空间,当请求到来时,这个潜入空间必须被选择使用。例如,如果用户公布了一个职位名称“Java”,我们将会加载训练的嵌入空间。当输入另一个未知的配置文件,例如说“Cobol Analyst”时,则使用预先训练的词嵌入。另外,对于每个CV和职位请求,计算其平均字嵌入向量。最后,我们只是检索与职位描述要求相匹配的前三名的简历。以下Python函数负责这个处理块:
model1 = Word2Vec.load(join(APP_STATIC, "word2vec/ourModel"))
with open(join(APP_STATIC, 'word2vec/reduced_pca.pickle'), 'rb') as f:
model2 = pickle.load(f)
@app.route('/find/', methods=['GET'])
def find():
data = request.args.get('value')
w2v = []
aux = data.lower().split(" ")[0:5]
sel = len(set(['java','sap','tester','prueba','hcm','sd','pruebas','testing']).intersection(aux))
val = False
if sel > 0:
model = model1
val = True
else:
model = model2
if val:
data = data.lower()
for sentence in es.parsetree(data, tokenize=True, lemmata=True, tags=True):
for chunk in sentence.chunks:
for word in chunk.words:
if val:
if word.lemma in model.wv.vocab:
w2v.append(model.wv[word.lemma])
else:
if word.lemma.lower() in model.wv.vocab:
w2v.append(model.wv[word.lemma.lower()])
else:
if word.string in model.keys():
w2v.append(model[word.string])
else:
if word.string.lower() in model.keys():
w2v.append(model[word.string.lower()])
Q_w2v = np.mean(w2v, axis=0)
# Example of document represented by average of each document term vectors.
dircvs = APP_STATIC + "/cvs_dir"
dircvsd = [join(dircvs, f) for f in listdir(dircvs) if isfile(join(dircvs, f))]
D_w2v = []
for cv in dircvsd:
yd = textract.process(cv).decode('utf-8')
w2v = []
for sentence in es.parsetree(yd.lower(), tokenize=True, lemmata=True, tags=True):
for chunk in sentence.chunks:
for word in chunk.words:
if val:
if word.lemma in model.wv.vocab:
w2v.append(model.wv[word.lemma])
else:
if word.lemma.lower() in model.wv.vocab:
w2v.append(model.wv[word.lemma.lower()])
else:
if word.string in model.keys():
w2v.append(model[word.string])
else:
if word.string.lower() in model.keys():
w2v.append(model[word.string.lower()])
D_w2v.append((np.mean(w2v, axis=0),cv))
# Make the retrieval using cosine similarity between query and document vectors.
retrieval = []
for i in range(len(D_w2v)):
retrieval.append((1 - spatial.distance.cosine(Q_w2v, D_w2v[i][0]),D_w2v[i][1]))
retrieval.sort(reverse=True)
ret_data = {"cv1":url_for('static', filename="test/"+retrieval[0][1][retrieval[0][1].rfind('/')+1:]), "score1": str(round(retrieval[0][0], 4)), "cv2":url_for('static', filename="test/"+retrieval[1][1][retrieval[1][1].rfind('/')+1:]), "score2": str(round(retrieval[1][0], 4)),"cv3":url_for('static', filename="test/"+retrieval[2][1][retrieval[2][1].rfind('/')+1:]), "score3": str(round(retrieval[2][0], 4)) }
return jsonify(ret_data)
- 如何使用CDSW在CDH中分布式运行所有R代码
- 如何在CDH中使用HBase快照
- 中间件安全-Tomcat安全测试概要
- 如何在CDH集群使用HDFS快照
- Sentry赋予server1权限给hive以外用户时ACL不同步问题分析
- 如何使用Java连接Kerberos的HBase
- 香香的xss小记录(一)
- UAF Writeup - pwnable.kr
- 如何使用Nginx实现Impala负载均衡
- pwnhub年前最后一战——“血月归来”writeup
- MHN中心服务器搭建与树莓派蜜罐部署
- 如何在CDH中启用Spark Thrift
- 让你的以太坊 DApps 盈利的 6 种方法
- 赛博地球杯初赛第三名,ChaMd5安全团队的writeup
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 基于qiankun落地部署微前端爬”坑“记
- Android推送的群魔乱舞
- 用百度接口实现图片文字识别,并打包成安装包软件
- 视野前端(二)V8引擎是如何工作的
- 【干货】Chrome插件(扩展)开发全攻略
- 超性感的React Hooks(一):为何她独具魅力
- 超性感的React Hooks(二)再谈闭包
- Python全栈(一)基础之11.函数(3)
- Python全栈(二)数据结构和算法之1.算法和数据结构引入
- Android开发(第一行代码 第二版) 常见异常和解决办法(基于Android Studio)(一)
- Python SQLite 基本操作和经验技巧(一)
- Python字典及基本操作(超级详细)
- Python matplotlab库使用方法及注意事项
- 超性感的React Hooks(三):useState
- 数据库编程 MySQL 技巧与经验