(福建工程學(xué)院 信息科學(xué)與工程學(xué)院,福建 福州 350118)
快消品,快速消費(fèi)品(FMCG,fast moving consumer goods)的簡(jiǎn)稱,是指那些使用壽命較短,消費(fèi)速度較快的消費(fèi)品??煜放粕淘诰€下渠道面臨困境,從線下轉(zhuǎn)向線上來(lái)尋求額外的銷售增長(zhǎng),是行業(yè)內(nèi)最顯著的趨勢(shì)。近年來(lái),互聯(lián)網(wǎng)快消品市場(chǎng)迎來(lái)迅速發(fā)展,涌現(xiàn)出一大批快消品電商網(wǎng)站,如阿里1688零售通、京東掌柜寶、掌合天下、惠民網(wǎng)、便利寶、易酒批、萬(wàn)全速配、中國(guó)郵政郵樂(lè)網(wǎng)等[1]。
因?yàn)榭煜肥褂脡勖^短、價(jià)格較為便宜,消費(fèi)者的購(gòu)買決策往往相對(duì)簡(jiǎn)單,更加熱衷于品牌化、大眾化。然而快消品牌種類成千上萬(wàn),如果能從電商網(wǎng)站的搜索日志里挖掘出熱搜品牌并推薦給用戶,將大大降低用戶的時(shí)間成本。但是,用戶在搜索時(shí)輸入的關(guān)鍵字往往不總是商品完整的品牌名稱,所以如何從搜索引擎的每一條搜索日志里挖掘出與品牌檢索熱度間的潛在相關(guān)性成為問(wèn)題的關(guān)鍵。
層面檢索(facet search),也稱層面導(dǎo)航,是一種按照分類法進(jìn)行存儲(chǔ)和檢索信息的技術(shù)[2]。層面檢索能夠在搜索關(guān)鍵字的同時(shí),按照Facet的字段進(jìn)行分組并統(tǒng)計(jì),主要用于導(dǎo)航實(shí)現(xiàn)漸進(jìn)式精確搜索,從而給用戶提供更加友好的搜索體驗(yàn)。比如在京東或淘寶的搜索欄輸入“筆記本電腦”進(jìn)行搜索,在搜索結(jié)果頁(yè)面上方會(huì)展示品牌、內(nèi)存容量、屏幕尺寸等不同類目的相關(guān)查詢結(jié)果。用戶可以通過(guò)點(diǎn)擊這些類目里的結(jié)果進(jìn)行漸進(jìn)式精確搜索。這里的品牌、內(nèi)存容量、屏幕尺寸就是一個(gè)個(gè)Facet。目前絕大多數(shù)的電商網(wǎng)站搜索引擎都有提供層面搜索功能。而且常用的幾大開(kāi)源搜索引擎框架,如Apache Solr、ElasticSearch也都實(shí)現(xiàn)了層面搜索功能。層面搜索使得搜索引擎可對(duì)數(shù)據(jù)之間的內(nèi)在聯(lián)系進(jìn)行挖掘,從而作為海量數(shù)據(jù)的統(tǒng)計(jì)工具。
MapReduce技術(shù)是Google公司于2004年提出的一種分布式編程模型,主要用于大規(guī)模數(shù)據(jù)集的并行計(jì)算[3]。MapReduce采用了分而治之的思想,將復(fù)雜的、運(yùn)行于大規(guī)模集群上的并行計(jì)算過(guò)程高度地抽象到兩個(gè)函數(shù):Map和Reduce。谷歌的MapReduce運(yùn)行在GFS(google file system,谷歌文件系統(tǒng))上,Hadoop MapReduce是谷歌的MapReduce的開(kāi)源實(shí)現(xiàn),運(yùn)行在HDFS(hadoop distributed file system,Hadoop分布式文件系統(tǒng))上。如無(wú)特殊說(shuō)明,本文所提的MapReduce均為Hadoop MapReduce。
數(shù)據(jù)本地化是MapReduce的核心特征,即采用“計(jì)算向數(shù)據(jù)靠攏”的設(shè)計(jì)理念[4],因?yàn)樵诖髷?shù)據(jù)集群環(huán)境下,移動(dòng)數(shù)據(jù)需要大量的網(wǎng)絡(luò)傳輸開(kāi)銷,而移動(dòng)計(jì)算則比移動(dòng)數(shù)據(jù)更加經(jīng)濟(jì)。本著這個(gè)理念,在一個(gè)集群中,MapReduce框架會(huì)盡量讓Map程序就近地在HDFS數(shù)據(jù)所在的節(jié)點(diǎn)運(yùn)行,即盡量將計(jì)算節(jié)點(diǎn)與數(shù)據(jù)節(jié)點(diǎn)放在一起運(yùn)行,從而大大減少了數(shù)據(jù)在節(jié)點(diǎn)間的移動(dòng)開(kāi)銷,有效提升整體性能。
MapReduce極大地方便了分布式編程工作,它把計(jì)算過(guò)程分解為兩個(gè)階段,即Map階段和Reduce階段。程序員只需實(shí)現(xiàn)Map函數(shù)和Reduce函數(shù),而如分布式存儲(chǔ)、集群任務(wù)調(diào)度、節(jié)點(diǎn)通信、負(fù)載均衡、容錯(cuò)處理等并行編程中的各種復(fù)雜問(wèn)題則由MapReduce框架負(fù)責(zé)解決。Map函數(shù)和Reduce函數(shù)都是以鍵值對(duì)
表1 Map和Reduce函數(shù)[5]Tab.1 Map and Reduce functions[5]
具體的MapReduce工作流程如圖1所示,詳細(xì)描述如下[4,6-11]:
(1)在Map階段,將存儲(chǔ)在HDFS上的輸入文件邏輯切分為多個(gè)等大小的分片。每個(gè)分片即為一個(gè)塊(Block)的大小,默認(rèn)為128M。
(2)因?yàn)镠DFS上每個(gè)塊默認(rèn)保存3個(gè)副本,Map任務(wù)會(huì)盡量就近讀取輸入數(shù)據(jù)分片,并從中解析出一個(gè)鍵值對(duì)集合,作為Map任務(wù)的輸入。
(3)Map任務(wù)會(huì)根據(jù)用戶自定義的映射規(guī)則,輸出一系列的鍵值對(duì)
(4)為了讓Reduce任務(wù)可以并行處理Map的輸出結(jié)果,需要對(duì)Map的輸出進(jìn)行混洗(Shuffle),即進(jìn)行分區(qū)(Partitioin)、排序(Sort)、合并(Combine)、歸并(Merge)等操作,以得到
(5)在Reduce階段,Reduce任務(wù)以一系列
圖1 MapReduce的工作流程Fig.1 MapReduce workflow
實(shí)際應(yīng)用中,很多復(fù)雜的問(wèn)題很難用一輪MapReduce任務(wù)解決,需要將其拆分成多個(gè)MapReduce子任務(wù)去完成。由于后一個(gè)子任務(wù)要使用前一個(gè)子任務(wù)的輸出結(jié)果,所以經(jīng)常在一輪 MapReduce 任務(wù)執(zhí)行完成之后,其輸出并不合并成一個(gè)文件,而是直接作為下一輪MapReduce 任務(wù)的輸入,從而構(gòu)成迭代的MapReduce工作流[12-13]。
用戶在電商網(wǎng)站檢索商品時(shí),輸入的關(guān)鍵字往往并不是商品的品牌名稱,為了從用戶的每次檢索行為統(tǒng)計(jì)出品牌檢索熱度,本文在搜索引擎的檢索日志里增加記錄每次用戶檢索的結(jié)果集在商品品牌字段上的層面統(tǒng)計(jì),格式如圖2所示。
圖2 品牌層面檢索日志格式Fig.2 Log format of facet search on brand
日志文件中每行為一條檢索日志記錄,每條檢索日志記錄包含多個(gè)屬性,各個(gè)屬性由符號(hào)‘|’分隔。圖2中第一個(gè)屬性表示檢索時(shí)間;第二個(gè)屬性表示檢索關(guān)鍵字;第三個(gè)屬性“brandFacet”是品牌層面統(tǒng)計(jì)的標(biāo)識(shí),說(shuō)明下一個(gè)屬性表示本次檢索的結(jié)果集在品牌字段上的層面統(tǒng)計(jì);第四個(gè)屬性表示層面統(tǒng)計(jì),由若干個(gè)鍵值對(duì)組成,一個(gè)鍵值對(duì)中的鍵和值用符號(hào)‘:’分隔,鍵值對(duì)間用空格分隔。例如圖2中第一行的含義為用戶在2018-12-30 23:19:39輸入關(guān)鍵字“康師傅”進(jìn)行檢索,本次檢索在品牌層面的統(tǒng)計(jì)結(jié)果為“00006:66”,即共檢索出66個(gè)品牌編碼為“00006”(“00006”是康師傅的品牌編碼,品牌名稱與品牌編碼的對(duì)應(yīng)關(guān)系存儲(chǔ)于字典表中)的商品。圖2所示的檢索日志格式還可以擴(kuò)展為每行同時(shí)記錄多維層面統(tǒng)計(jì)結(jié)果。
接下來(lái)根據(jù)每次檢索的品牌層面統(tǒng)計(jì)結(jié)果計(jì)算出本次檢索對(duì)該結(jié)果集中各品牌熱度的貢獻(xiàn)值。具體計(jì)算過(guò)程如公式(1)所示。
(1)
其中,Hi表示某次檢索對(duì)品牌i的熱度貢獻(xiàn)值,Ni表示該次檢索結(jié)果集中品牌i的商品數(shù)目,n表示該次檢索結(jié)果集中品牌的數(shù)目??梢?jiàn)Hi的取值范圍是(0,1],其值越大,表示本次檢索對(duì)品牌i的熱度貢獻(xiàn)值越大。
最后,將某個(gè)時(shí)間段內(nèi)的檢索日志對(duì)各品牌的熱度貢獻(xiàn)值進(jìn)行歸并累加和排序即可得到該時(shí)間段內(nèi)各品牌的搜索熱度排行榜。
從搜索引擎的檢索日志統(tǒng)計(jì)熱搜品牌需要進(jìn)行三輪的MapReduce作業(yè)。第一輪MapReduce作業(yè)完成各個(gè)品牌的搜索熱度的計(jì)算,輸出一系列鍵值對(duì)<品牌編碼,搜索熱度>。第二輪MapReduce作業(yè)將第一輪作業(yè)的輸出與品牌字典表進(jìn)行連接操作,將品牌編碼信息轉(zhuǎn)換為“品牌編碼|品牌名稱”。第三輪MapReduce作業(yè)對(duì)第二輪作業(yè)的輸出按value(即品牌搜索熱度)的值進(jìn)行降序排序輸出。
第一輪MapReduce作業(yè)的輸入為搜索引擎的檢索日志文件,讀取日志里的每條品牌層面統(tǒng)計(jì)結(jié)果,在Map階段按照公式(1)算出該次檢索對(duì)檢索結(jié)果集中各品牌的熱度貢獻(xiàn)值,在Reduce階段對(duì)相同品牌的熱度貢獻(xiàn)值進(jìn)行累加。第一輪MapReduce作業(yè)的map和reduce函數(shù)的具體實(shí)現(xiàn)代碼如下:
map(LongWritable ikey,Text ivalue,Context context){
String readline = ivalue.toString();
/*取每行的第四個(gè)屬性(即品牌層面統(tǒng)計(jì)),利用空白符進(jìn)行分割得到一個(gè)字符串?dāng)?shù)組,如讀圖2第二行則brands數(shù)組包含兩個(gè)元素:”00005:33”和”00028:13”*/
String[] brands = readline.split("\|")[3].split("\s+");
float sum = 0;
/*算出每次檢索結(jié)果集中商品的總數(shù)量,如讀圖2第二行則sum=46 */
for(int i=0;i { if(brands[i].contains(":")) sum += Float.parseFloat(brands[i].split(":")[1]); else return; } /*算出每次檢索對(duì)檢索結(jié)果集中各品牌的熱度貢獻(xiàn)值,如讀圖2第二行則輸出<”00005”,33/46>,<”00028”,13/46>*/ for(int i=0;i { String[] brand = brands[i].split(":"); context.write(new Text(brand[0]),new FloatWritable(Float.parseFloat(brand[1])/sum)); } } /*reduce函數(shù):接收 reduce(Text _key,Iterable float sum =0; /*對(duì)品牌的搜索熱度值進(jìn)行累加*/ for (FloatWritable val :values){ sum += val.get(); } /*輸出鍵值對(duì)<品牌編碼,搜索熱度>*/ context.write(_key,new FloatWritable(sum)); } 第二輪MapReduce作業(yè)實(shí)現(xiàn)將第一輪作業(yè)的輸出與品牌字典表的連接操作。因?yàn)槠放谱值浔韮H存儲(chǔ)品牌編碼與品牌名稱的映射,其數(shù)據(jù)集足夠小到可以完全放到緩存中,所以這里采用MapReduce提供的復(fù)制連接(Replication join)策略。復(fù)制連接常用于大數(shù)據(jù)集與小數(shù)據(jù)集的連接操作,它是一種Map端連接,省去Shuffle和Reduce的過(guò)程,大大降低了作業(yè)運(yùn)行時(shí)間。復(fù)制連接的基本思路如下: (1)在main方法中調(diào)用Job對(duì)象的addCacheFile(URI uri)方法將品牌字典表復(fù)制到所有運(yùn)行map任務(wù)的節(jié)點(diǎn)的緩存中。其中uri為品牌字典表在HDFS上的地址。 (2)在各個(gè)map任務(wù)的setup方法中調(diào)用context.getCacheFiles()從緩存中取出這個(gè)品牌字典表,裝載到一個(gè)哈希表brandMap中。 (3)在map函數(shù)中遍歷哈希表進(jìn)行連接操作。 (4)輸出結(jié)果(即沒(méi)有Reduce階段)。 第二輪MapReduce作業(yè)的map函數(shù)的具體實(shí)現(xiàn)代碼如下: map(LongWritable ikey,Text ivalue,Context context){ Stringreadline = ivalue.toString(); /*讀取第一輪MapReduce作業(yè)的輸出并用‘ ’分隔得到的數(shù)組reads有兩個(gè)元素,reads[0]表示品牌編碼,reads[1]表示搜索熱度*/ String[] reads = readline.split(" "); //如品牌編碼不在字典表中,則不統(tǒng)計(jì) if(brandMap.get(reads[0])==null)return; kout.set(reads[0]+”|”+brandMap.get(reads[0])); /*輸出鍵值對(duì)<品牌編碼|品牌名稱,搜索熱度>*/ context.write(kout,new FloatWritable- (Float.parseFloat(reads[1]))); } 第三輪MapReduce作業(yè)實(shí)現(xiàn)將第二輪作業(yè)的輸出按照value(即品牌搜索熱度)的值進(jìn)行降序排序并取TOP-N。因?yàn)樵贛ap端的Shuffle過(guò)程中會(huì)對(duì)map函數(shù)的輸出按照key做升序的默認(rèn)排序?,F(xiàn)要按照value進(jìn)行排序,所以第三輪MapReduce作業(yè)的map函數(shù)要實(shí)現(xiàn)將key和value互換,即輸入 map(LongWritable ikey,Text ivalue,Context context){ String[] reads=ivalue.toString().split(" "); FloatWritable kOut=new FloatWritable(); TextvOuT= new Text(); kOut.set(Float.parseFloat(reads[1])); vOut.set(reads[0]); /*輸出鍵值對(duì)<搜索熱度,品牌編碼|品牌名稱>*/ context.write(kOut,vOut); } reduce(FloatWritable _key,Iterable /*輸出鍵值對(duì)<品牌編碼|品牌名稱,搜索熱度>*/ for (Text val :values){ //搜索熱度值保留兩位小數(shù) context.write(val,new FloatWritable- ((float)(Math.round(_key.get()*100))/100)); } } 在上面的reduce函數(shù)中是把所有的鍵值對(duì)<品牌編碼|品牌名稱,搜索熱度>都輸出到HDFS,當(dāng)然如果只需輸出TOP-N,則可以定義一個(gè)變量充當(dāng)循環(huán)變量,在for循環(huán)里輸出N次即可。 在福建工程學(xué)院大數(shù)據(jù)教學(xué)服務(wù)器上虛擬化出9個(gè)節(jié)點(diǎn),在這9個(gè)節(jié)點(diǎn)上搭建Hadoop分布式集群。實(shí)驗(yàn)放在該集群上運(yùn)行,集群中每個(gè)節(jié)點(diǎn)的硬件配置為2核CPU,8G內(nèi)存,操作系統(tǒng)為Ubuntu 16.04,Hadoop版本為原生的Hadoop 2.7.5,JDK版本為1.8。 集群中每個(gè)節(jié)點(diǎn)的主機(jī)名和IP地址如表2所示。 實(shí)驗(yàn)數(shù)據(jù)采用便利寶電商網(wǎng)站(www.wqblb.com)2018年12月份的檢索日志數(shù)據(jù),當(dāng)月的檢索日志共有3 327 413條檢索記錄,日志文件總大小為1 124MB。品牌字典表文件共含7 748條記錄,文件大小為124 kB。 圖3是分別在3、5、7、9個(gè)節(jié)點(diǎn)的集群環(huán)境下運(yùn)行完整工作流的響應(yīng)時(shí)間。從圖3可以看出在9個(gè)節(jié)點(diǎn)的集群環(huán)境下響應(yīng)時(shí)間只要28.64 s,滿足批處理的響應(yīng)需求。 表2 節(jié)點(diǎn)的主機(jī)名和IP地址Tab.2 Host name and IP address of node 圖3 MapReduce工作流的響應(yīng)時(shí)間Fig.3 Response time of MapReduce workflow 在實(shí)驗(yàn)中,設(shè)置N為10。獲取到搜索熱度TOP-10的品牌列表為{00006|康師傅,00606|悅巢,00005|可口可樂(lè),00031|晨光,00037|心心相印,01450|保為康,00007|統(tǒng)一,00008|達(dá)利園,00290|伊利,00011|農(nóng)夫山泉}。為了驗(yàn)證實(shí)驗(yàn)計(jì)算出的品牌搜索熱度排名的準(zhǔn)確性,可以和該期間各品牌產(chǎn)生的成交量排名對(duì)比,如圖4所示。各品牌產(chǎn)生的成交量排名可以通過(guò)查詢訂單明細(xì)表,按品牌編碼分組統(tǒng)計(jì)成交量排名。 從圖4可看出,品牌搜索熱度排名和成交量排名大致相當(dāng)。從而驗(yàn)證了從搜索引擎日志里挖掘品牌搜索熱度排名榜的可行性。 圖4 品牌搜索熱度排名和成交量排名對(duì)比Fig.4 Comparison of brand search popularing rankings and trading volume rankings 快消品電商網(wǎng)站搜索引擎的檢索日志記錄著用戶的檢索偏好,本文根據(jù)檢索日志里的品牌層面統(tǒng)計(jì)結(jié)果挖掘出用戶每次檢索對(duì)檢索結(jié)果集中各品牌的搜索熱度的貢獻(xiàn)值,設(shè)計(jì)一個(gè)迭代的MapReduce工作流用于計(jì)算網(wǎng)站某時(shí)間段內(nèi)的各品牌的搜索熱度總值排名,從而可以在網(wǎng)站適當(dāng)?shù)胤较蛴脩敉扑]熱搜品牌。在未來(lái)的研究中,將引入Spark技術(shù),以實(shí)現(xiàn)熱搜品牌的實(shí)時(shí)個(gè)性化推薦。3.2 第二輪MapReduce作業(yè)的算法設(shè)計(jì)
3.3 第三輪MapReduce作業(yè)的算法設(shè)計(jì)
4 實(shí)驗(yàn)
4.1 實(shí)驗(yàn)環(huán)境
4.2 實(shí)驗(yàn)數(shù)據(jù)
4.3 響應(yīng)時(shí)間
4.4 TOP-N分析
5 結(jié)語(yǔ)