php分词结果怎么用,有哪些不为人知但是很有意思的网站?
平时休息在家,你有什么打算呢?
如果你没有特别的打算,甚至无聊得想数头发,不如打开电脑看看这些有趣的网站,脑洞大开的也是没sei了!
http://www.emoji.zone
当无数的emoji表情连续不断向你扑来是一种怎样的体验?
这个怪咖网站将所有的表情汇聚在一起冲击你的屏幕,简直热辣滚烫,一阵酸爽,绝对值得体验一把。
不过,还是提醒各位...适(ting)可(bu)而(xia)止(lai)。
http://www.catflixx.com
这个简直是喵星控的福音,它收集了来自网络上各种囧猫搞笑的视频,学习累了工作倦了就看一把。
有甜甜的喵星人温暖你的心,也有高冷的喵妇让你笑得欲罢不能,保证你意犹未尽。
http://www.flakes.b-reel.com
没见到雪的小伙伴们不要着急,下面来个牛x的网站——制作雪花。
网站通过敲击键盘生成不规则漂亮的雪花,成功敲击出来的雪花都会被记录,看到满意的还可以生成短链接分享给好友!
期待各位的成果哦!
http://www.midomi.com
哼歌搜歌曲的神器就在这里了!
它的使用特别简单,只要首先点击页面最上方的“Click and Sing or Hum”按钮,在弹出的Flash窗口中点击“允许”,接下来通过麦克风将自己想要的歌曲清唱出来(嘿嘿,不会词也没关系,只要把节奏哼出来就行),30秒钟之后,网站会自动停止录制并开始进行旋律比对。
稍后,一个根据哼唱结果匹配出来的歌曲列表会自动显示出来,而列表顶端往往就是那个曾让自己千寻万找的曲子!
英语歌词咬不准的,你的机会来了,大胆哼出来吧!
http://www.kuaidula.com
平时我们阅读的时候是眼球在动,但是这个网站可以让我们在阅读的时候不用动眼球,只要盯在一个地方阅读。
因为屏幕会自动滚动的呀。
感兴趣的可以上去体验一把,懒得连眼睛都不想动的人,我只能帮你到这里了。
http://www.flashearth.com
这是一个以上帝视角看地球的网站,一点一点放大,你甚至可以看到你家门口拥堵的情况。
http://www.airpano.com
360度高清晰无死角可任意放大缩小的网站,让你站在难以企及的视角观察壮美绝伦的风光!
“等了好久终于等到今天,梦了好久终于把梦实现”恩,在家就可以游遍全世界啦。
搭配上当地的民族乐曲,我仿佛感觉我已经来这里走过。
http://weavesilk.com
只要随手一画,就能对称成形,产生酷炫的画作!
如果你是从小就想当画家的但又苦于命运安排,这个网站可以创造你的神作。
你以为小编的水平就这样?下面这个才是我的大作。
江山代有才人出,你也可以来一发。
http://staggeringbeauty.com
最后这个压轴的网站,它是一只可以被人调戏的温柔害羞的蚯蚓。
跟着你的鼠标摆动,身体灵活度五颗星,看起来十分的可爱。
可是,当你的鼠标剧烈晃动时,画风突然变了,还有动感的背景音乐......
用爬虫技术能做到哪些有趣的事情?
看到这个问题必须来怒答一波~用python爬虫爬便宜机票了解一下?
喜欢旅行又怕吃土?让Python来爬取最便宜机票吧!图源:
videoblocks.com
你喜欢旅行吗?
这个问题通常会得到一个肯定的答案,随后引出一两个有关之前冒险经历的故事。大多数人都认为旅行是体验新文化和开阔视野的好方法。但如果问题是“你喜欢搜索机票的过程吗?”也许话题就到此为止了……
可事实上,便宜的机票往往也很重要!本文将尝试构建一个网络爬虫,该爬虫对特定目的地运行并执行带有浮动日期(首选日期前后最多三天)的航班价格搜索。它会将结果保存为excel文件并发送一封包含快速统计信息的电子邮件。显然,这个爬虫的目的就是帮助我们找到最优惠的价格!
你可以在服务器上运行脚本(一个简单的Raspberry Pi就可以),每天运行一到两次。结果会以邮件形式发送,建议将excel文件存入Dropbox文件夹,以便随时随地查看。
因为爬虫以“浮动日期”进行搜索,所以它会搜索首选日期前后最多三天的航班信息。尽管该脚本一次仅运行一对目的地,但可以很容易地改写该爬虫使其每个循环运行多个目的地。最终甚至可能找到一些错误票价...那会很有意思!
另一个爬虫某种意义上来讲,网络爬取是互联网“工作”的核心。
也许你认为这是一个十分大胆的说法,但谷歌就是从拉里·佩奇用Java和Python构建的网络爬虫开始的。爬虫不断地爬取信息,整个互联网都在试图为所有问题提供最佳的可能答案。网络爬取有不计其数的应用程序,即使更喜欢数据科学中的其他分支,你仍需要一些爬取技巧以获得数据。
这里用到的一些技术来自于最近新的一本佳作《Python网络数据采集》,书中包含与网络爬取相关的所有内容,并提供了大量简例和实例。甚至有一个特别有意思的章节,讲述如何解决验证码检验的问题。
Python的拯救第一个挑战就是选择爬取信息的平台,本文选择了客涯(Kayak)。我们试过了Momondo, 天巡(Skyscanner), 亿客行(Expedia)和其它一些网站,但是这些网站上的验证码特别变态。
在那些“你是人类吗?”的验证中,尝试了多次选择交通灯、十字路口和自行车后,客涯似乎是最好的选择,尽管短时间内加载太多页面它会跳出安全检查。
我们设法让机器人每4到6个小时查询一次网站,结果一切正常。虽然说不定哪个部分偶尔会出点小问题,但是如果收到验证码,既可以手动解决问题后启动机器人,也可以等待几小时后的自动重启。
如果你是网络爬取新手,或者不知道为何有些网站花费很大力气阻止网络爬取,那么为构建爬虫写下第一行代码前,你一定要多加努力。
谷歌的“网络爬取规范”:
http://lmgtfy.com/?q=web+scraping+etiquette
系紧安全带...导入并打开Chrome浏览器标签页后,会定义一些循环中会用到的函数。这个架构的构思大概是这样的:
· 一个函数用于启动机器人程序,表明想要搜索的城市和日期。
· 这个函数获得首轮搜索结果,按“最佳”航班排序,然后点击“加载更多结果”。
· 另一个函数会爬取整个页面,并返回一个dataframe数据表。
· 随后重复步骤2和步骤3,得出按“价格”和“航行时间”排序的结果。
· 发送一封简要价格(最低价和平均价)的邮件,并将带有这三种排序类型的dataframe数据表保存为一份excel文件。
· 以上所有步骤会在循环中重复,每X小时运行一次。
每个Selenium项目都以一个网页驱动器开始。我们使用Chromedriver驱动器,但还有其它选择。PhantomJS和Firefox也很受欢迎。下载Chromedriver后,将其置于一个文件夹中即可。第一行代码会打开一个空白Chrome标签页。
from time import sleep, strftime
from random import randint
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import smtplib
from email.mime.multipart import MIMEMultipart
# Change this to your own chromedriver path!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'
driver = webdriver.Chrome(executable_path=chromedriver_path) # This will open the Chrome window
sleep(2)
这些是将用于整个项目的包。使用randint函数令机器人在每次搜索之间随机睡眠几秒钟。这对任何一个机器人来说都是必要属性。如果运行前面的代码,应该打开一个Chrome浏览器窗口,机器人会在其中导航。
一起来做一个快速测试:在另一个窗口上访问客涯网(http://kayak.com),选择往返城市和日期。选择日期时,确保选择的是“+-3天”。由于在编写代码时考虑到了结果页面,所以如果只想搜索特定日期,很可能需要做一些微小的调整。
点击搜索按钮在地址栏获取链接。它应该类似于下面所使用的链接,将变量kayak定义为url,并从网页驱动器执行get方法,搜索结果就会出现。
无论何时,只要在几分钟内使用get命令超过两到三次,就会出现验证码。实际上可以自己解决验证码,并在下一次验证出现时继续进行想要的测试。从测试来看,第一次搜索似乎一直没有问题,所以如果想运行这份代码,并让它在较长的时间间隔后运行,必须解决这个难题。你并不需要十分钟就更新一次这些价格,对吧?
每个XPath都有陷阱到目前为止,已经打开了一个窗口,获取了一个网站。为了开始获取价格和其他信息,需要使用XPath或CSS选择器,我们选择了XPath。使用XPath导航网页可能会令人感到困惑,即使使用从inspector视图中直接使用“复制XPath”,但这不是获得所需元素的最佳方法。有时通过“复制XPath”这个方法获得的链接过于针对特定对象,以至于很快就失效了。《Python网络数据采集》一书很好地解释了使用XPath和CSS选择器导航的基础知识。
接下来,用Python选择最便宜的结果。上面代码中的红色文本是XPath选择器,在网页上任意一处右键单击选择“inspect”就可以看到它。在想要查看代码的位置,可以再次右键单击选择“inspect”。
为说明之前所观察到的从“inspector”复制路径的缺陷,请参考以下差异:
1 # This is what the copymethod would return. Right click highlighted rows on the right side and select “copy> Copy XPath”//*[@id=“wtKI-price_aTab”]/div[1]/div/div/div[1]/div/span/span
2 # This is what I used todefine the “Cheapest” buttoncheap_results= ‘//a[@data-code = “price”]’
第二种方法的简洁性清晰可见。它搜索具有data-code等于price属性的元素a。第一种方法查找id等于wtKI-price_aTab的元素,并遵循第一个div元素和另外四个div和两个span。这次……会成功的。现在就可以告诉你,id元素会在下次加载页面时更改。每次页面一加载,字母wtKI会动态改变,所以只要页面重新加载,代码就会失效。花些时间阅读XPath,保证你会有收获。
不过,使用复制的方法在不那么“复杂”的网站上工作,也是很好的!
基于以上所展示的内容,如果想在一个列表中以几个字符串的形式获得所有搜索结果该怎么办呢?其实很简单。每个结果都在一个对象中,这个对象的类是“resultWrapper”。获取所有结果可以通过像下面这样的for循环语句来实现。如果你能理解这一部分,应该可以理解接下来的大部分代码。它基本上指向想要的结果(结果包装器),使用某种方式(XPath)获得文本,并将其放置在可读对象中(首先使用flight_containers,然后使用flight_list)。
前三行已展示在图中,并且可以清楚地看到所需的内容,但是有获得信息的更优选择,需要逐一爬取每个元素。
准备起飞吧!最容易编写的函数就是加载更多结果的函数,所以代码由此开始。为了在不触发安全验证的前提下最大化所获取的航班数量,每次页面显示后,单击“加载更多结果”。唯一的新内容就是所添加的try语句,因为有时按钮加载会出错。如果它对你也有用,只需在前面展示的start_kayak函数中进行简要注释。
# Load more results to maximize the scraping
def load_more():
try:
more_results = '//a[@class = “moreButton”]'
driver.find_element_by_xpath(more_results).click()
# Printing these notes during the program helps me quickly check what it is doing
print('sleeping…..')
sleep(randint(45,60))
except:
pass
现在,经过这么长的介绍,已经准备好定义实际爬取页面的函数。
我们编译了下一个函数page_scrape中的大部分元素。有时这些元素会返回列表插入去程信息和返程信息之间。这里使用了一个简单的办法分开它们,比如在第一个 section_a_list和section_b_list变量中,该函数还返回一个flight_df数据表。所以可以分离在不同分类下得到的结果,之后再把它们合并起来。
def page_scrape():
“““This function takes care of the scraping part”““
xp_sections = '//*[@class=“section duration”]'
sections = driver.find_elements_by_xpath(xp_sections)
sections_list = [value.text for value in sections]
section_a_list = sections_list[::2] # This is to separate the two flights
section_b_list = sections_list[1::2] # This is to separate the two flights
# if you run into a reCaptcha, you might want to do something about it
# you will know there's a problem if the lists above are empty
# this if statement lets you exit the bot or do something else
# you can add a sleep here, to let you solve the captcha and continue scraping
# i'm using a SystemExit because i want to test everything from the start
if section_a_list == []:
raise SystemExit
# I'll use the letter A for the outbound flight and B for the inbound
a_duration = []
a_section_names = []
for n in section_a_list:
# Separate the time from the cities
a_section_names.append(''.join(n.split()[2:5]))
a_duration.append(''.join(n.split()[0:2]))
b_duration = []
b_section_names = []
for n in section_b_list:
# Separate the time from the cities
b_section_names.append(''.join(n.split()[2:5]))
b_duration.append(''.join(n.split()[0:2]))
xp_dates = '//div[@class=“section date”]'
dates = driver.find_elements_by_xpath(xp_dates)
dates_list = [value.text for value in dates]
a_date_list = dates_list[::2]
b_date_list = dates_list[1::2]
# Separating the weekday from the day
a_day = [value.split()[0] for value in a_date_list]
a_weekday = [value.split()[1] for value in a_date_list]
b_day = [value.split()[0] for value in b_date_list]
b_weekday = [value.split()[1] for value in b_date_list]
# getting the prices
xp_prices = '//a[@class=“booking-link”]/span[@class=“price option-text”]'
prices = driver.find_elements_by_xpath(xp_prices)
prices_list = [price.text.replace('$','') for price in prices if price.text != '']
prices_list = list(map(int, prices_list))
# the stops are a big list with one leg on the even index and second leg on odd index
xp_stops = '//div[@class=“section stops”]/div[1]'
stops = driver.find_elements_by_xpath(xp_stops)
stops_list = [stop.text[0].replace('n','0') for stop in stops]
a_stop_list = stops_list[::2]
b_stop_list = stops_list[1::2]
xp_stops_cities = '//div[@class=“section stops”]/div[2]'
stops_cities = driver.find_elements_by_xpath(xp_stops_cities)
stops_cities_list = [stop.text for stop in stops_cities]
a_stop_name_list = stops_cities_list[::2]
b_stop_name_list = stops_cities_list[1::2]
# this part gets me the airline company and the departure and arrival times, for both legs
xp_schedule = '//div[@class=“section times”]'
schedules = driver.find_elements_by_xpath(xp_schedule)
hours_list = []
carrier_list = []
for schedule in schedules:
hours_list.append(schedule.text.split('\n')[0])
carrier_list.append(schedule.text.split('\n')[1])
# split the hours and carriers, between a and b legs
a_hours = hours_list[::2]
a_carrier = carrier_list[1::2]
b_hours = hours_list[::2]
b_carrier = carrier_list[1::2]
cols = (['Out Day', 'Out Time', 'Out Weekday', 'Out Airline', 'Out Cities', 'Out Duration', 'Out Stops', 'Out Stop Cities',
'Return Day', 'Return Time', 'Return Weekday', 'Return Airline', 'Return Cities', 'Return Duration', 'Return Stops', 'Return Stop Cities',
'Price'])
flights_df = pd.DataFrame({'Out Day': a_day,
'Out Weekday': a_weekday,
'Out Duration': a_duration,
'Out Cities': a_section_names,
'Return Day': b_day,
'Return Weekday': b_weekday,
'Return Duration': b_duration,
'Return Cities': b_section_names,
'Out Stops': a_stop_list,
'Out Stop Cities': a_stop_name_list,
'Return Stops': b_stop_list,
'Return Stop Cities': b_stop_name_list,
'Out Time': a_hours,
'Out Airline': a_carrier,
'Return Time': b_hours,
'Return Airline': b_carrier,
'Price': prices_list})[cols]
flights_df['timestamp'] = strftime(“%Y%m%d-%H%M”) # so we can know when it was scraped
return flights_df
尽量让这些名字容易理解。记住变量a表示旅行的去程信息,变量b表示旅行的返程信息。接下来说说下一个函数。
等等,还有什么吗?截至目前,已经有了一个能加载更多结果的函数和一个能爬取其他结果的函数。本可以在此结束这篇文章,而你可以自行手动使用这些函数,并在浏览的页面上使用爬取功能。但是前文提到给自己发送邮件和一些其他信息的内容,这都包含在接下来的函数start_kayak中。
它要求填入城市名和日期,并由此打开一个kayak字符串中的地址,该字符串直接跳转到“最佳”航班结果排序页面。第一次爬取后,可以获取价格的顶部矩阵,这个矩阵将用于计算平均值和最小值,之后和客涯(Kayak)的预测结果(页面左上角)一同发送到邮件中。这是单一日期搜索时可能导致错误的原因之一,因其不包含矩阵元素。
def start_kayak(city_from, city_to, date_start, date_end):
“““City codes it's the IATA codes!
Date format YYYY-MM-DD”““
kayak = ('https://www.kayak.com/flights/' + city_from + '-' + city_to +
'/' + date_start + '-flexible/' + date_end + '-flexible?sort=bestflight_a')
driver.get(kayak)
sleep(randint(8,10))
# sometimes a popup shows up, so we can use a try statement to check it and close
try:
xp_popup_close = '//button[contains(@id,”dialog-close”) and contains(@class,”Button-No-Standard-Style close “)]'
driver.find_elements_by_xpath(xp_popup_close)[5].click()
except Exception as e:
pass
sleep(randint(60,95))
print('loading more.....')
# load_more()
print('starting first scrape.....')
df_flights_best = page_scrape()
df_flights_best['sort'] = 'best'
sleep(randint(60,80))
# Let's also get the lowest prices from the matrix on top
matrix = driver.find_elements_by_xpath('//*[contains(@id,”FlexMatrixCell”)]')
matrix_prices = [price.text.replace('$','') for price in matrix]
matrix_prices = list(map(int, matrix_prices))
matrix_min = min(matrix_prices)
matrix_avg = sum(matrix_prices)/len(matrix_prices)
print('switching to cheapest results…..')
cheap_results = '//a[@data-code = “price”]'
driver.find_element_by_xpath(cheap_results).click()
sleep(randint(60,90))
print('loading more…..')
# load_more()
print('starting second scrape…..')
df_flights_cheap = page_scrape()
df_flights_cheap['sort'] = 'cheap'
sleep(randint(60,80))
print('switching to quickest results…..')
quick_results = '//a[@data-code = “duration”]'
driver.find_element_by_xpath(quick_results).click()
sleep(randint(60,90))
print('loading more…..')
# load_more()
print('starting third scrape…..')
df_flights_fast = page_scrape()
df_flights_fast['sort'] = 'fast'
sleep(randint(60,80))
# saving a new dataframe as an excel file. the name is custom made to your cities and dates
final_df = df_flights_cheap.append(df_flights_best).append(df_flights_fast)
final_df.to_excel('search_backups//{}_flights_{}-{}_from_{}_to_{}.xlsx'.format(strftime(“%Y%m%d-%H%M”),
city_from, city_to,
date_start, date_end), index=False)
print('saved df…..')
# We can keep track of what they predict and how it actually turns out!
xp_loading = '//div[contains(@id,”advice”)]'
loading = driver.find_element_by_xpath(xp_loading).text
xp_prediction = '//span[@class=“info-text”]'
prediction = driver.find_element_by_xpath(xp_prediction).text
print(loading+'\n'+prediction)
# sometimes we get this string in the loading variable, which will conflict with the email we send later
# just change it to “Not Sure” if it happens
weird = '¯\\_(ツ)_/¯'
if loading == weird:
loading = 'Not sure'
username = 'YOUREMAIL@hotmail.com'
password = 'YOUR PASSWORD'
server = smtplib.SMTP('smtp.outlook.com', 587)
server.ehlo()
server.starttls()
server.login(username, password)
msg = ('Subject: Flight Scraper\n\n\
Cheapest Flight: {}\nAverage Price: {}\n\nRecommendation: {}\n\nEnd of message'.format(matrix_min, matrix_avg, (loading+'\n'+prediction)))
message = MIMEMultipart()
message['From'] = 'YOUREMAIL@hotmail.com'
message['to'] = 'YOUROTHEREMAIL@domain.com'
server.sendmail('YOUREMAIL@hotmail.com', 'YOUROTHEREMAIL@domain.com', msg)
print('sent email…..')
虽然没有使用Gmail账户测试发送邮件,但是可以搜索到很多的替代方法,前文提到的那本书中也有其他方法来实现这一点。如果已有一个Hotmail账户,只要替换掉个人的详细信息,它就会开始工作了。
如果想探索脚本的某一部分正在做什么,可以将脚本复制下来并在函数外使用它。这是彻底理解它的唯一方法。
利用刚才创造的一切在这些步骤之后,还可以想出一个简单的循环来使用刚创造的函数,同时使其持续运行。完成四个“花式”提示,写下城市和日期(输入)。因为测试时不想每次都输入这些变量,需要的时候可以使用以下这个清楚的方式进行替换。
如果已经做到了这一步,恭喜你!改进还有很多,比如与Twilio集成,发送文本消息而不是邮件。也可以使用VP*或更加难懂的方式同时从多个服务器上研究搜索结果。还有就是验证码的问题,验证码会时不时地跳出来,但对此类问题还是有解决办法的。不过,能走到这里已经是有很牢固的基础了,你可以尝试添加一些额外的要素。
使用脚本运行测试的示例
留言 点赞 关注
我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”
Aso新手没人带?
ASO优化,是真正展现了“台上一分钟,台下十年功”这一句的真理。
想要从事这个行业,就必须清楚为什么需要做ASO,以及做ASO的目的是什么?
为什么需要做ASO?
ASO是“应用商店优化”的简称。就是提升你APP在各类APP应用商店/市场排行榜和搜索结果排名的过程。
ASO优化就是利用App Store的搜索规则和排名规则让APP更容易被用户搜索或看到。
因为App Store的分发量分布于:搜索、榜单、推荐位、外部导量。而70%以上来源于用户主动搜索关键词。通过ASO,下载量较优化前能上涨50%-300%不等。
ASO的目的是什么?
ASO的最终目的是通过更多的展示获取更多的流量。开发者可以通过榜单、搜索、推荐这些不同的方式实现App在App Store中的曝光量,提升APP在应用商店的流量和下载转化率,最终目的是获得更多的用户。
那么,作为一个纯小白,从没有涉及到这个领域的小白,应该从哪些方面开始呢?
一、关键词
ASO优化重要的一点就是关键词,可以简单的理解为当你在IOS上想到的任何词类,这个产品都会出现在你眼前,那说明这个关键词你的App就覆盖上了。关键词覆盖范围越广,你的App 就有越多的机会展示给用户,下载量当然也会随波逐涨啦。
二、关键词覆盖
一般需要提供3套100 字符的方案,当然3这个数字是前辈们依据App Store 的关键词覆盖算法不断摸索出来,是对提升中国的关键词覆盖有效果的,依次为中国中文,英国英文和澳大利亚英文,其中重要的权重又以中国中文为大,其它两套权重顺延。
CP们在iTC (如上图)上传或更新产品时,可以填写多个版本在多个国家和地区填写关键词,但是究竟填写多少个国家或地区,填写哪些国家和地区,需要我们这些后生们,根据苹果算法的迭代不断的探索,对于中国区关键词覆盖真正的生效,以此来提升产品的关键词覆盖。
三、了解产品分析
要做出一个完美的优化方案,要先充分了解你所做的产品。亲自下载,亲自体验,亲自了解,之后就是分析啦,通过现有的平台,关于产品四面八方的信息,尽你所能去搜集去整理去分类,应用信息、评分评论和榜单实时排名等。这些,都可以通过现有的工具去查验。
四、选择关键词的方法
首先需要了解实力竞品的关键词覆盖情况,选出5-6个竞品,根据热度值及排名,对关键词进行分析,通过筛选排重,选出热度较高或带量多的关键词,然后通过排列组合,组成3套词。
五、如何排列关键词
首先知道关键词的位置权重,越靠前关键词权重越大;其次,将选出的关键词进行分类,根据品牌词,竞品词,行业词,通用词,衍生词,相关词,高热度词等分列,然后对这些词根据热度,搜索指数等进行重组,最后再经过组词、扩词和改词,通过不断测试方案迭代让App 覆盖更多更精准的关键词。
ASO优化是一个长期持续优化的过程,需要不断的学习和积累。
个人观点,仅供参考,更多关于ASO的相关技巧和问题,请朋友们关注我,愿与您一起分享、讨论!
word2vec词向量加权的方法有哪些?
一、理论概述 (主要来源于http://licstar.net/archives/328这篇博客) 1.词向量是什么 自然语言理解的问题要转化为机器学习的问题,第一步肯定是要找一种方法把这些符号数学化。 NLP 中最直观,也是到目前为止最常用的词表示方法是 One-hot Representation,这种方法把每个词表示为一个很长的向量。这个向量的维度是词表大小,其中绝大多数元素为 0,只有一个维度的值为 1,这个维度就代表了当前的词。 举个栗子, “话筒”表示为 [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ...] “麦克”表示为 [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ...] 每个词都是茫茫 0 海中的一个 1。 这种 One-hot Representation 如果采用稀疏方式存储,会是非常的简洁:也就是给每个词分配一个数字 ID。比如刚才的例子中,话筒记为 3,麦克记为 8(假设从 0 开始记)。如果要编程实现的话,用 Hash 表给每个词分配一个编号就可以了。这么简洁的表示方法配合上最大熵、SVM、CRF 等等算法已经很好地完成了 NLP 领域的各种主流任务。 当然这种表示方法也存在一个重要的问题就是“词汇鸿沟”现象:任意两个词之间都是孤立的。光从这两个向量中看不出两个词是否有关系,哪怕是话筒和麦克这样的同义词也不能幸免于难。 Deep Learning 中一般用到的词向量并不是刚才提到的用 One-hot Representation 表示的那种很长很长的词向量,而是用 Distributed Representation(不知道这个应该怎么翻译,因为还存在一种叫“Distributional Representation”(类似,LDA中用topic表示词语的词向量的表示方法)表示的一种低维实数向量。这种向量一般是这个样子:[0.792, −0.177, −0.107, 0.109, −0.542, ...]。维度以 50 维和 100 维比较常见。 2.词向量的来历 Distributed representation 最早是 Hinton 在 1986 年的论文《Learning distributed representations of concepts》中提出的。虽然这篇文章没有说要将词做 Distributed representation但至少这种先进的思想在那个时候就在人们的心中埋下了火种,到 2000 年之后开始逐渐被人重视。 3. 词向量的训练 要介绍词向量是怎么训练得到的,就不得不提到语言模型。到目前为止我了解到的所有训练方法都是在训练语言模型的同时,顺便得到词向量的。 这也比较容易理解,要从一段无标注的自然文本中学习出一些东西,无非就是统计出词频、词的共现、词的搭配之类的信息。而要从自然文本中统计并建立一个语言模型,无疑是要求最为精确的一个任务(也不排除以后有人创造出更好更有用的方法)。既然构建语言模型这一任务要求这么高,其中必然也需要对语言进行更精细的统计和分析,同时也会需要更好的模型,更大的数据来支撑。目前最好的词向量都来自于此,也就不难理解了。 词向量的训练最经典的有 3 个工作,C&W 2008、M&H 2008、Mikolov 2010。当然在说这些工作之前,不得不介绍一下这一系列中 Bengio 的经典之作 4. 词向量的评价 词向量的评价大体上可以分成两种方式,第一种是把词向量融入现有系统中,看对系统性能的提升;第二种是直接从语言学的角度对词向量进行分析,如相似度、语义偏移等。 4.1 提升现有系统 词向量的用法最常见的有两种: 1. 直接用于神经网络模型的输入层。如 C&W 的 SENNA 系统中,将训练好的词向量作为输入,用前馈网络和卷积网络完成了词性标注、语义角色标注等一系列任务。再如 Socher 将词向量作为输入,用递归神经网络完成了句法分析、情感分析等多项任务。 2. 作为辅助特征扩充现有模型。如 Turian 将词向量作为额外的特征加入到接近 state of the art 的方法中,进一步提高了命名实体识别和短语识别的效果。 4.2 语言学评价 还有一个有意思的分析是 Mikolov 在 2013 年刚刚发表的一项发现。他发现两个词向量之间的关系,可以直接从这两个向量的差里体现出来。向量的差就是数学上的定义,直接逐位相减。比如 C(king)−C(queen)≈C(man)−C(woman)。更强大的是,与 C(king)−C(man)+C(woman) 最接近的向量就是 C(queen)。 为了分析词向量的这个特点, Mikolov 使用类比(analogy)的方式来评测。如已知 a 之于 b 犹如 c 之于 d。现在给出 a、b、c,看 C(a)−C(b)+C(c) 最接近的词是否是 d。 在文章 Mikolov 对比了词法关系(名词单复数 good-better:rough-rougher、动词第三人称单数、形容词比较级最高级等)和语义关系(clothing-shirt:dish-bowl) 这些实验结果中最容易理解的是:语料越大,词向量就越好。其它的实验由于缺乏严格控制条件进行对比,谈不上哪个更好哪个更差。不过这里的两个语言学分析都非常有意思,尤其是向量之间存在这种线性平移的关系,可能会是词向量发展的一个突破口。 关于Deep Lerning In Nlp的一些相关论文,《Deep Learning in NLP (一)词向量和语言模型》(http://licstar.net/archives/328)这篇博客的非常的好。以上内容大多数都是截取原博客内容。 二、实际操作 这篇文章是最近几天看word2vec源码以及相关神经网络训练词向量论文之后的个人小小的,主要是针对word2vec的使用,做一下介绍。望大家使用的过程中,少走弯路。 word2vec工具中包含了对两种模型的训练,如下图。在训练每种模型的时候又分HS和NEG两种方法。(看图就可以发现,其实word2vec并不deep……) 除了google自己的word2vec工具,各位对词向量感兴趣的牛人们也相继编写了各自不同的版本。其中比较好用的是Python Gensim主题模型包中的word2vec,但通过阅读其源码python版本只实现了skip-gram模型,并且只实现了通过分层softmax方法对其训练,并没有使用negative sampling。下面列举一下目前出现的版本以及相对应的地址,供大家选择。如下表: 版本 地址 CBOW Skip-Gram C http://word2vec.googlecode.com/svn/trunk/ HS NEG HS NEG python http://radimrehurek.com/gensim/ HS Java https://github.com/ansjsun/Word2VEC_java HS HS C++ https://github.com/jdeng/word2vec 未知 未知 未知 未知 以上代码,C++版本的我没有看过。最权威的当然是C语言版本,但是阅读起来比较困难一点。Python版本有优化处理,所以速度相对来说也不慢,但只是实现了分层softmax方法对skip-gram模型进行训练。Java版本分别实现了分层softmax方法对CBOW模型和skip-gram模型进行训练。C++版本的没有阅读其代码,所以未知…… 使用之前,先贴一些论文中对两个模型和不同方法的评价图片,方便大家根据不同任务进行不同训练。 下面以c语言正式版本为例,来介绍word2vec的使用。 首先我们将google word2vec项目源码checkout 到本机,具体地址是http://word2vec.googlecode.com/svn/trunk/ 使用ssh登录实验室Linux服务器,地址192.168.1.143。将刚才checkout的文件,上传到服务器中。 进入目录,命令行输入make指令,进行编译。 这样我们就可以开始使用,word2vec工具了。 1.将文本语料进行分词,以空格,tab隔开都可以,中文分词工具可以使用张华平博士的NLPIR2013 http://ictclas.nlpir.org/ 喜欢用Python 的童鞋也可以使用结巴分词https://github.com/fxsjy/jieba。 2.将分好词的训练语料进行训练,假定我语料名称为test.txt且在word2vec目录中。输入命令: ./word2vec -train test.txt -output vectors.bin -cbow 0 -size 200 -window 5 -negative 0 -hs 1 -sample 1e-3 -threads 12 -binary 1 以上命令表示的是输入文件是test.txt,输出文件是vectors.bin,不使用cbow模型,默认为Skip-Gram模型。 每个单词的向量维度是200,训练的窗口大小为5就是考虑一个词前五个和后五个词语(实际代码中还有一个随机选窗口的过程,窗口大小<=5)。不使用NEG方法,使用HS方法。-sampe指的是采样的阈值,如果一个词语在训练样本中出现的频率越大,那么就越会被采样。-binary为1指的是结果二进制存储,为0是普通存储(普通存储的时候是可以打开看到词语和对应的向量的)除了以上命令中的参数,word2vec还有几个参数对我们比较有用比如-alpha设置学习速率,默认的为0.025.–min-count设置最低频率,默认是5,如果一个词语在文档中出现的次数小于5,那么就会丢弃。-classes设置聚类个数,看了一下源码用的是k-means聚类的方法。 · 架构:skip-gram(慢、对罕见字有利)vs CBOW(快) · 训练算法:分层softmax(对罕见字有利)vs 负采样(对常见词和低纬向量有利) · 欠采样频繁词:可以提高结果的准确性和速度(适用范围1e-3到1e-5) · 文本(window)大小:skip-gram通常在10附近,CBOW通常在5附近 3.训练好模型之后,得到vectors.bin这个模型文件。vectors.bin这个文件就是文档中词语和其对应的向量,这个向量的维度是你训练时设置的参数大小。下面我们可以利用这个model做很多自然语言处理的任务了。Google代码里面提供了distance的一个应用,说白了就是读取模型文件中每一个词和其对应的向量,计算所输入query的词,与其他所有词语的cosine相似度,排序,返回结果。同理训练命令中的-classes参数,也是获取每个词对应的向量之后,对词语进行k-means聚类。对于训练出来的模型进行操作,我推荐大家使用http://blog.csdn.net/zhaoxinfan/article/details/11640573这个java版本的模型读取类,比较方便,读取模型之后大家就可以来实现各种各样有趣的东西了。下面举几个例子: a.计算两个词语相似度,如图1计算asp与net的相似度为0.6215127 图1 b.列出所有相似词语列表,如图2为“php”的结果。 图二 c.寻找对应关系:如图3: 男人-男孩 女人-? 如图4:内蒙-呼和浩特 河北-? 图3 图4 d.删选不合群的词语,可以用来做特征选择:如图5所示: