記錄近期小改Apriori至MapReduce上的心得

·背景 ios

    前一陣,一直在研究一些ML的東東,後來工做關係暫停了一陣。如今繼續把剩下一些熱門的算法再吃吃透,"無聊+逗比"地把他們搞到MapReduce上。此次選擇的入手對象爲Apriori,也就是你們俗稱的"關聯規則挖掘",有別於CF(協同過濾)的正交輸出。再俗一點,就是常被人說起的"啤酒+麪包"的故事。 c++

 

·Apriori算法簡介 算法

    在關聯規則挖掘方面,有兩項著名的算法:Apriori和FPgrowth。二者各有特色,因爲計算量級別的差別,愈來愈多的人選擇了後者。但這並不意味着Apriori就是垃圾。我的的理解,FPgrowth爲MP而生,Apriori爲容器而生。當單日誌量達到5G,10G以上Apriori在計算方面的吃虧逐步顯現,即使如此,對於人們對於儘量減小Apriori掃描次數的優化機制仍然樂此不疲。尤爲是做爲容器方面的選擇,能夠極大的減小整個代碼的實現過程和增長可讀性,同時又能訓練你的腦力和對容器的使用創意。 centos

 

做爲單機版原本說,總體Apriori的邏輯過程如上所述,相比"樹"和"圖"來講要簡單許多,可是仍然暗藏很多重複計算的陷阱。 數據結構

  1. 基本條件,有交易記錄T和本身設定的支持度support。
  2. 從1維最小元素開始作group by+count(*),獲得統計集合
  3. 按support條件,把不符合條件的統計對象過濾掉,相似oracle having追加條件。
  4. 把渣過濾完畢後,作徹底組合,也就是你們在初中時候學的徹底惟一組合,獲得頻繁項組合。這其中你也能夠本身設個條件,認爲XXX就是最大的頻繁項。
  5. 以這頻繁項爲基礎,去掃描交易記錄T,獲得步驟2。以此反覆,找到你想要的關聯組合。

 

做爲MP版來講,Apriopi如何壓榨Map的資源,真是件使人頭痛的事情。如上圖所述: oracle

  1. 每一個Map DataNode都會獲得最大的頻繁項。若是總體交易記錄構成是徹底隨機分佈,那最後的C的數量會很是集中並且穩定。
  2. 不幸的是,你沒法保證十幾,幾十G的交易T中的交易習慣,必定是隨機均勻分佈,若是是正態、泊松、二項式等等。那C中最大項集合數量會猛增。
  3. 這時候,必須在從Map DataNode中繼續過一遍集合,刪除干擾度較高的最大頻繁項,儘可能找到惟一最大頻繁。

 

· 一些心得體會 函數

  1. 就像拍照同樣,要先構圖。整段代碼在把小孩子哄睡後花了近一週時間才完成。其中6成時間用於數據結構設計,3成時間用來code,1成用來調試。
  2. 數據結構返工兩次,最後劍走偏鋒。樓主逗比地用了二維set,網上都了一把,用的人很少,不是那種new出來的不帶屬性那種,而是set套set,和map套set。極盡c++容器之能事。感受好的數據結構,至少少寫200行代碼。代碼思路: C[頻繁set<set>]-> [Struct/raw_c] -> Map<set,int> -> L -> set<set> 
  3. 如何減小計算量是件噁心的事,把文本入內存須要一次N量計算,掃面一次就是N量。整個Apriorio的計算至少K·N,着實笨重,這還不算很多代碼潔癖的愛好,還會反覆疊加計算量。
  4. 附屬的是一個單機版的Apriori,MP的就補貼了,思路如上圖所述。在生產環境中還須要考慮到hadoop的輸出限制和函數返回值形式,須要儘量使用引用和指針,減小內存交互。
  5. 官網有提到支持度這個概念,固然你們也能夠徹底按照本身的意願作一些改動和設定,條條大道去羅馬,基本就這個模式了。

· 單機版   centos 6(2.6)+gcc433oop

#include <iostream>
#include <sstream>
#include <fstream>
#include <string.h>
#include <vector>
#include <map>
#include <set>
#include <algorithm>

using namespace std; 

typedef struct {                                                          //原始集合結構定義
   vector<string> ss;
} t_raw_jh;

typedef struct {                              //有效數據/統計集合定義 
   set<string> ss;
   int sup_num;
} t_raw_tj;


t_raw_jh   raw_c[50];                         //原始數據
map<set<string>,int> raw_cnt_base;            //原始元素統計集合
t_raw_tj   L[10];                             //洗出來的單集合組合
//set<set>   new_kc;                          //洗出來的排列對象集,做爲下次掃描的催化劑
int raw_num=0;                                //原始數據計數器
int l_num=0;                                  //統計數據計算器
int raw_msup=2;                               //頻繁項支持度

/* C[頻繁set<set>]-> [Struct/raw_c] -> Map<set,int> -> L -> set<set> */

void CountEleBase(string line,const char* delim,int raw_num);   //1+pre,把一條元素洗到二維顆粒,同時統計group by count
void CountEleReal(set<set<string> > &cc);                       //1+real,根據3的催化劑,掃描原始記錄,同時統計group by count
int WashEleBase();                                              //2,把MAP SORT[group by count]洗出大於支持度的對象集
set<set<string> > GetNewKc(int size);                           //3,把洗出的對象集合,極限排列出全部可能,做爲下次掃描的催化劑
void Display();                                                 //顯示原始集合、單元素非排序group by count
void ClearTj();                                                 //把統計表給清空
void RunApriori(set<set<string> > &fc);                         //嵌套跑,固然也能夠迭代WHILE跑,入參爲頻繁項


int main()
{
         std::ios::sync_with_stdio(false);
         string filename="./ap001.txt";
         string line;
         ifstream ifs;
         ifs.open(filename.c_str());
         
         while(getline(ifs,line))
         {
                 CountEleBase(line,",",raw_num);
                 raw_num++;
         }

        // Display();
    //獲得第一組通過支持度過濾的C 
        set<set<string> > aaa;   //頻繁項,根據有效支持元素得出的組合
        aaa=GetNewKc(WashEleBase());

        RunApriori(aaa);

}

void RunApriori(set<set<string> > &fc)
{
    if(l_num==1) return;
    CountEleReal(fc);
        fc.clear();
        fc=GetNewKc(WashEleBase());
        RunApriori(fc);
}

void CountEleReal(set<set<string> > &cc)
{
          //掃描記錄集
          cout<<"\n-----掃描記錄集-----"<<endl;
          //迭代器定義好,在該函數內以後會用到
          set<set<string> >::iterator ot_it;
          set<string>::iterator in_it;
          raw_cnt_base.clear();

          for(ot_it=cc.begin();ot_it!=cc.end();ot_it++)
          {
                 int map_cc=0; 
                 for(int i=0;i<raw_num;i++)
             {
                        vector<string>::iterator res; 
                        for(res=raw_c[i].ss.begin();res!=raw_c[i].ss.end();res++)
                        {
                                cout<<*res<<" ";
                        }
                        cout<<"|";
                        int map_in_cc=0;
                        for(in_it=(*ot_it).begin();in_it!=(*ot_it).end();in_it++)
                        {
                                cout<<*in_it<<" ";
                                res=find(raw_c[i].ss.begin(),raw_c[i].ss.end(),*in_it);
                                if(res!=raw_c[i].ss.end()) map_in_cc++;
                        }
                        if(map_in_cc==(*ot_it).size()) map_cc++;
                        cout<<"|"<<map_in_cc<<endl;
                 }
                 raw_cnt_base[*ot_it]=map_cc;
                 cout<<"-->"<<map_cc<<endl;
      }
}

set<set<string> > GetNewKc(int size)
{
           vector<string>::iterator ss_it;
           set<string> kk[size];
           set<set<string> >  new_kc;
           int pi=0;

           set<string>::iterator kk_it;
           //作兩兩極限排列組合
           for(int i=0;i<l_num;i++)
           {
               set<string> tmp=L[i].ss;
               for(int j=i+1;j<l_num;j++)
               {
                                for(kk_it=L[j].ss.begin();kk_it!=L[j].ss.end();kk_it++)
                                {
                                      tmp.insert(*kk_it); 
                            }
                        new_kc.insert(tmp);
                                tmp=L[i].ss;
           }
       }

           cout<<"有效支持元素 ---> C組合[頻繁項]:"<<endl;
           set<set<string> >::iterator out_it;
           set<string>::iterator in_it;
           for(out_it=new_kc.begin();out_it!=new_kc.end();out_it++)
           {
                for(in_it=(*out_it).begin();in_it!=(*out_it).end();in_it++)
                {
                   cout<<*in_it<<",";
                }
                cout<<endl;
           }
           return  new_kc;
}



int WashEleBase()
{
        map<set<string>,int>::iterator raw_cnt_it;
    int i=0;
        l_num=0;
        ClearTj();
        for(raw_cnt_it=raw_cnt_base.begin();raw_cnt_it!=raw_cnt_base.end();raw_cnt_it++)
        {
           if(raw_cnt_it->second>=raw_msup)
           {
                   L[i].ss=raw_cnt_it->first;
                   L[i++].sup_num=raw_cnt_it->second;
                   l_num++;
           }
        }

        //Display
        cout<<"有效支持元素:"<<endl;
        set<string>::iterator ss_it;
        for(i=0;i<l_num;i++)
        {
                for(ss_it=L[i].ss.begin();ss_it!=L[i].ss.end();ss_it++)
                {
                        cout<<*ss_it<<",";
                }
                cout<<"|"<<L[i].sup_num<<endl;
        }
        return i;
}


void CountEleBase(string line,const char* delim,int raw_num)
{
                 char *p=NULL,*q=NULL;
                 p=const_cast<char*>(line.c_str());
                 while(p)
                 {
                         q=strsep(&p,",");
                         set<string> load_ss;
                         raw_c[raw_num].ss.push_back(q);
                         load_ss.insert(q); raw_cnt_base[load_ss]++;             
                 }
}

void Display()
{
         cout<<"----Print Map-------"<<endl;
         map<set<string>,int>::iterator raw_cnt_it;
         set<string>::iterator ss_it;
         for(raw_cnt_it=raw_cnt_base.begin();raw_cnt_it!=raw_cnt_base.end();raw_cnt_it++)
         {
                 for(ss_it=(raw_cnt_it->first).begin();ss_it!=(raw_cnt_it->first).end();ss_it++)
                 {
                        cout<<*ss_it<<" ";
                 }
                 cout<<":"<<raw_cnt_it->second<<endl;
         }

         //raw_cnt_base.clear();

         cout<<"----Print Raw-------"<<endl;
         vector<string>::iterator raw_c_it;
         for(int i=0;i<raw_num;i++)
     {
                 for(raw_c_it=raw_c[i].ss.begin();raw_c_it!=raw_c[i].ss.end();raw_c_it++)
                 {
                          cout<<*raw_c_it<<" ";
                 }
                 cout<<endl;
         }
}

void ClearTj()
{
        for(int i=0;i<10;i++)
        {
                L[i].sup_num=0;
                L[i].ss.clear();
        }

}

  

· 另優化

    望各位路過的大俠,嘴上留情,手上斧正,看看可否進一步壓縮計算空間量~~。^_^。 spa

相關文章
相關標籤/搜索