コード例 #1
0
def groupby(from_dtm, to_dtm, tmp_dir, out_path, num_chunks=10):
    from_dtm, to_dtm = map(str, [from_dtm, to_dtm])
    fouts = {
        idx: open(os.path.join(tmp_dir, str(idx)), 'w')
        for idx in range(num_chunks)
    }
    files = sorted([path for path, _ in iterate_data_files(from_dtm, to_dtm)])
    for path in tqdm.tqdm(files, mininterval=1):
        for line in open(path):
            user = line.strip().split()[0]
            chunk_index = mmh3.hash(user, 17) % num_chunks
            fouts[chunk_index].write(line)

    map(lambda x: x.close(), fouts.values())
    with open(out_path, 'w') as fout:
        for chunk_idx in fouts.keys():
            _groupby = {}
            chunk_path = os.path.join(tmp_dir, str(chunk_idx))
            for line in open(chunk_path):
                tkns = line.strip().split()
                userid, seen = tkns[0], tkns[1:]
                _groupby.setdefault(userid, []).extend(seen)
            os.remove(chunk_path)
            for userid, seen in six.iteritems(_groupby):
                fout.write('%s %s\n' % (userid, ' '.join(seen)))
コード例 #2
0
    def _get_seens(self, users):
        """_get_seens는 사용자들이 본 @유저의 목록을 구성하는 함수입니다.
        Args:
            users   : 브런치 (사용자해시값)의 list 입니다.
        Return:
            seens   : {(사용자해시값) : [@이 사용자가 본 유저의 목록]}로 구성된 dict입니다.
        """
        #사용자 목록의 중복을 제거합니다.
        set_users = set(users)
        seens = {}

        #from_dtm과 to_dtm 사이의 있는 파일의 경로를 받아 반복합니다.
        for path, _ in tqdm.tqdm(iterate_data_files(self.from_dtm,
                                                    self.to_dtm),
                                 mininterval=1):
            #경로에 있는 파일을 열어서 모든 line을 차례대로 읽습니다.
            #line은 (사용자의 해시값) (@사용자가 열람한 유저들) 로 이뤄진 하나의 문자열입니다.
            for line in open(path):
                #(사용자의 해시값)과 (@사용자가 열람한 유저들)로 구성된 리스트를 만듭니다.
                tkns = line.strip().split()
                #userid 는 (사용자의 해시값)입니다.
                #seen은 (@사용자가 열람한 유저들)로 구성된 리스트입니다.
                userid, seen = tkns[0], tkns[1:]

                #유저 아이디가 사용자 목록에 없다면 그냥 넘어갑니다.
                if userid not in set_users:
                    continue
                #유저 아이디가 사용자 목록에 있다면
                #seens는 userid를 키로 갖고 seen을 값으로 갖도록 합니다.
                #seens = {userid : [seen]}
                seens[userid] = seen
        return seens
コード例 #3
0
def read_reads():
    files = sorted(
        [path for path, _ in iterate_data_files('2018100100', '2019030100')])
    for path in tqdm.tqdm(files, mininterval=1):
        date = path[11:19]
        for line in open(path):
            tokens = line.strip().split()
            user_id = tokens[0]
            reads = tokens[1:]
            if user_id in t_users:
                if user_id in t_reads:
                    t_reads[user_id] += reads
                else:
                    t_reads[user_id] = reads
                if date >= "20190222":
                    if user_id in t_reads_dup:
                        t_reads_dup[user_id] += reads
                    else:
                        t_reads_dup[user_id] = reads
            else:
                if user_id in reads:
                    o_reads[user_id] += reads
                else:
                    o_reads[user_id] = reads
                if date >= "20190222":
                    if user_id in reads_dup:
                        reads_dup[user_id] += reads
                    else:
                        reads_dup[user_id] = reads
コード例 #4
0
def groupby(from_dtm,
            to_dtm,
            tmp_dir,
            out_path,
            num_chunks=10):  # 2018100100 2019022200 ./tmp/ ./tmp/train
    from_dtm, to_dtm = map(
        str, [from_dtm, to_dtm
              ])  # function, 리스트를 받아 리스트 인자들을 함수에 넣은 결과를 다시 리스트로 반환한다.
    fouts = {
        idx: open(os.path.join(tmp_dir, str(idx)), 'w')
        for idx in range(num_chunks)
    }  # 딕셔너리 형태로 파일의 객체를 저장, 경로는 tmp_dir\idx
    files = sorted([path for path, _ in iterate_data_files(from_dtm, to_dtm)
                    ])  # _ 언더 스코어로 두번째 인자값 무시하고 path만 쓴다, 오름차순대로 정렬된 리스트를 갖는다
    # print(files) 리스트 첫번째 인자 : './res/read\\2018100100_2018100101', 리스트 마지막 인자 : './res/read\\2019022123_2019022200'
    # 유저마다 10개의 파일중 하나의 파일에 들어가게 된다 유저아이디 아티클
    for path in tqdm.tqdm(
            files, desc="files to path", mininterval=1
    ):  # tqdm으로 for문의 상태를 알 수 있다 실행시 상태바가 뜬다! 1초마다 상태바가 갱신된다
        for line in open(path):  # path의 파일을 열고 한줄마다 실행해준다
            user = line.strip().split()[0]  # 줄의 첫번째 단어가 유저
            chunk_index = mmh3.hash(
                user,
                17) % num_chunks  # 유저를 해쉬화하고 10으로 나눈값에 따라 다른 임시파일에 정보가 들어가게 된다
            fouts[chunk_index].write(
                line)  # 미리 만들어둔 딕셔너리로 chunk_index에 해당하는 키값의 파일에 해당 라인을 쓴다

    map(lambda x: x.close(), fouts.values(
    ))  # 딕셔너리의 value만 갖고 있는 dict_values객체를 받아 즉 파일들을 받고 map으로 인자를 접근해 전부 닫아준다

    # 파일마다 유저아이디에 따라 본 아티클을 통합해준다, 중복된 유저아이디가 없이 파일마다 유저 아이디 이후 본 아티클이 정리된다
    with open(
            out_path, 'w'
    ) as fout:  # 파일을 열고 사용한 후에 닫는 것을 보장하는 문법, try: finally: 구조나 file.__enter__(), __exit__()를 따로 구현한 코드를 안써도 된다. 반환값을 fout이 받는다
        for chunk_idx in fouts.keys():  # 키값에 대해서 모두 반복한다
            _groupby = {
            }  # 언더스코어는 private한 변수임을 의미 import했을때 접근하지 못하는정도의 private수준
            chunk_path = os.path.join(tmp_dir,
                                      str(chunk_idx))  # 키값을 붙인 새로운 path를 저장
            for line in open(chunk_path):  # 임시 path의 한 라인마다 실행해준다
                tkns = line.strip().split()  # 토큰별로 나누어준 리스트를 반환
                userid, seen = tkns[0], tkns[
                    1:]  # 토큰의 첫번째는 유저아이디이고 그 이후부터는 본 아티클을 얘기한다
                _groupby.setdefault(userid, []).extend(
                    seen
                )  # userid를 키값으로 설정하구 value는 []를 디폴트로 한다. 해당 키값 value에 seen의 리스트 내용을 뒤에 추가해준다
            for userid, seen in six.iteritems(
                    _groupby):  # 딕셔너리의 키값마다 반복하고 키값과 value값을 반환한다
                fout.write(
                    '%s %s\n' % (userid, ' '.join(seen))
                )  # 최종 out_path경로 파일에 유저아이디와 본 아티클을 스페이스로 구분하여서 한줄마다 쓴다
            os.remove(os.path.join(
                tmp_dir,
                str(chunk_idx)))  # 해당 경로의 파일을 삭제, 이유 모를 에러 발생 직접 삭제해주자...
コード例 #5
0
 def _get_seens(self, users):
     set_users = set(users)
     seens = {}
     for path, _ in tqdm.tqdm(iterate_data_files(self.from_dtm, self.to_dtm),
                              mininterval=1):
         for line in open(path):
             tkns = line.strip().split()
             userid, seen = tkns[0], tkns[1:]
             if userid not in set_users:
                 continue
             seens[userid] = seen
     return seens
コード例 #6
0
def groupby(from_dtm, to_dtm, tmp_dir, out_path, num_chunks=10):
    """groupby는 일정한 시간간격의 파일들을 합쳐 하나의 그룹 파일로 만드는 함수입니다.
    Args:
        from_dtm    : 시작날짜입니다.   YYYYMMDDHH
        to_dtm      : 종료날짜입니다.   YYYYMMDDHH
        tmp_dir     : 주요 파일이 저장되는 tmp 파일의 경로입니다.
        out_path    : 출력될 파일의 경로입니다.
        num_chunks  : 그룹화 되기전 만들어질 청크의 개수입니다.
    """
    #문자열을 받는, from_dtm부터 to_dtm까지의 맵 객체를 만들어 from_dtm과 to_dtm에 저장합니다.
    from_dtm, to_dtm = map(str, [from_dtm, to_dtm])

    #fouts은 {num_chucks : 쓰기로 열려있는 (tmp_dir/idx) }로 구성된 dict입니다.
    fouts = {
        idx: open(os.path.join(tmp_dir, str(idx)), 'w')
        for idx in range(num_chunks)
    }

    #files는 from_dtm과 to_dtm 사이에 있는 파일들의 리스트입니다,
    #시간순으로 정렬되어 있습니다.
    files = sorted([path for path, _ in iterate_data_files(from_dtm, to_dtm)])

    #path는 files에서 원소를 가져옵니다.
    for path in tqdm.tqdm(files, mininterval=1):
        #path의 한 라인을 읽습니다.
        #path의 한 라인은 다음과 같이 구성되어 있는 문자열 입니다.
        #(사용자의 해시값) (@사용자가 열람한 유저들)
        for line in open(path):
            #user은 (사용자의 해시값)을 가져옵니다.
            user = line.strip().split()[0]

            #user에 대해 hash값을 생성한 다음 num_chucks로 범위를 줄입니다.
            chunk_index = mmh3.hash(user, 17) % num_chunks
            #chunk_index 파일에 line을 씁니다.
            fouts[chunk_index].write(line)

    #fout으로 열려있는 파일을 모두 닫습니다.
    list(map(lambda x: x.close(), fouts.values()))

    #0~num_chunks 까지 만들어 놓은 임시 파일을 out_path로 grouping 하는 과정
    with open(out_path, 'w') as fout:
        for chunk_idx in fouts.keys():
            _groupby = {}
            chunk_path = os.path.join(tmp_dir, str(chunk_idx))
            for line in open(chunk_path):
                tkns = line.strip().split()
                userid, seen = tkns[0], tkns[1:]
                _groupby.setdefault(userid, []).extend(seen)
            os.remove(chunk_path)
            for userid, seen in six.iteritems(_groupby):
                fout.write('%s %s\n' % (userid, ' '.join(seen)))
    fout.close()
コード例 #7
0
def make_sentence():
  session_sentences = []
  prev_hour_reads = {}

  of1 = open("session_sentences_1h.txt", "w")
  of2 = open("session_sentences_2h.txt", "w")
  print("read reads for the test set")
  num_noread = 0
  files = sorted([path for path, _ in iterate_data_files('2018100100', '2019030100')])
  for path in tqdm.tqdm(files, mininterval=1):
    datehour = path[11:21]
    hour_reads = {}
    for line in open(path):
      tokens = line.strip().split()
      user_id = tokens[0]
      reads = tokens[1:]
      if len(reads) == 0:
          num_noread += 1
          continue
      ureads = [] # remove continuously doubled article
      prev_read = None
      for read in reads:
        if prev_read == None or read != prev_read:
          ureads.append(read)
        prev_read = read
      hour_reads[user_id] = ureads

      if len(ureads) > 1:
        of1.write(" ".join(ureads) + "\n")
    
    curr_hour_reads = hour_reads.copy()

    for user_id in hour_reads:
      if user_id in prev_hour_reads:
        if len(prev_hour_reads[user_id]) + len(hour_reads[user_id]) > 1:
          of2.write(" ".join(prev_hour_reads[user_id]) + " " + " ".join(hour_reads[user_id]) + "\n")
        del prev_hour_reads[user_id]
        del curr_hour_reads[user_id]

    for user_id in prev_hour_reads:
      if len(prev_hour_reads[user_id]) > 1:
        of2.write(" ".join(prev_hour_reads[user_id]) + "\n")

    prev_hour_reads = curr_hour_reads.copy()
    

  of1.close()
  of2.close()
  print("no read:", num_noread)
コード例 #8
0
    def _build_model(self):
        model_path = self._get_model_path()
        if os.path.isfile(model_path):
            return

        freq = {}
        print('building model..')
        for path, _ in tqdm.tqdm(iterate_data_files(self.from_dtm, self.to_dtm),
                                 mininterval=1):
            for line in open(path):
                seen = line.strip().split()[1:]
                for s in seen:
                    freq[s] = freq.get(s, 0) + 1
        freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
        open(model_path, 'wb').write(cPickle.dumps(freq, 2))
        print('model built')
コード例 #9
0
def read_files_to_dataframe(dest_path):
    print('read files of all users')
    data = []
    files = sorted(
        [path for path, _ in iterate_data_files('2018100100', '2019022200')])
    for path in tqdm(files, mininterval=1):
        read_datetime = path.split('/')[-1].split('_')[0][:9]
        for line in open(path):
            tokens = line.strip().split()
            user_id = tokens[0]
            reads = tokens[1:]
            for item in reads:
                data.append([read_datetime, user_id, item])
    read_df = pd.DataFrame(data)
    read_df.columns = ['date', 'user_id', 'article_id']
    read_df.to_csv(os.path.join(dest_path, 'read_df.csv'),
                   index=False,
                   encoding='utf-8-sig')
コード例 #10
0
    def _build_model(self):
        model_path = self._get_model_path()
        if os.path.isfile(model_path):
            return

        freq = {}
        print("building model..")
        for path, _ in tqdm.tqdm(
            iterate_data_files(self.from_dtm, self.to_dtm), mininterval=1
        ):
            for line in open(path):
                seen = line.strip().split()[1:]  # 어떤 유저가 조회한 글 목록
                for s in seen:
                    freq[s] = freq.get(s, 0) + 1  # 등장한 글 하나씩 빈도 추가
        freq = sorted(
            freq.items(), key=lambda x: x[1], reverse=True
        )  # [(글, 빈도), ...] 형태의 빈도 리스트
        open(model_path, "wb").write(cPickle.dumps(freq, 2))
        print("model built")
コード例 #11
0
def read_reads():
    print("read reads for the test set")
    files = sorted(
        [path for path, _ in iterate_data_files('2018100100', '2019030100')])
    for path in tqdm.tqdm(files, mininterval=1):
        date = path[11:19]
        for line in open(path):
            tokens = line.strip().split()
            user_id = tokens[0]
            reads = tokens[1:]
            if len(reads) < 1: continue
            if user_id in t_users:
                if user_id in t_reads:
                    t_reads[user_id] += reads
                else:
                    t_reads[user_id] = reads
                if date >= "20190222":
                    if user_id in t_reads_dup:
                        t_reads_dup[user_id] += reads
                    else:
                        t_reads_dup[user_id] = reads

                reads_set = set(reads)
                for read in reads_set:
                    writer = read.split("_")[0]
                    if user_id not in t_followings or writer not in t_followings[
                            user_id]:
                        if user_id in t_non_follows:
                            if writer in t_non_follows[user_id]:
                                t_non_follows[user_id][writer] += 1
                            else:
                                t_non_follows[user_id][writer] = 1
                        else:
                            t_non_follows[user_id] = {}
                            t_non_follows[user_id][writer] = 1

    for user in t_reads:
        if user not in t_reads_dup:
            t_reads_dup[user] = t_reads[user][-10:]
コード例 #12
0
    def _build_model(self):
        model_path = self._get_model_path()
        if os.path.isfile(model_path):  # 파일이 존재하면 끝내고 없으면 모델이 있는 파일을 만들어 준다
            return

        freq = {}
        print('building model...')
        # 기간내에 아티클마다 조회수가 저장되게 된다
        for path, _ in tqdm.tqdm(iterate_data_files(self.from_dtm,
                                                    self.to_dtm),
                                 mininterval=1):
            for line in open(path):
                seen = line.strip().split()[1:]
                for s in seen:
                    freq[s] = freq.get(
                        s, 0
                    ) + 1  # 해당 키값 s에 해당하는 value값에 1을 더한걸 value값으로 갱신 value값이 없는경우 0으로 초기화해서 1더한걸 리턴
        freq = sorted(freq.items(), key=lambda x: x[1],
                      reverse=True)  # value값에 따른 내림차순으로 정렬
        # print(freq) # ('@hotelscombined_399', 1) 와 같은 인자를 가진 리스트로 저장
        open(model_path, 'wb').write(cPickle.dumps(freq, 2))
        print('model built')
コード例 #13
0
    def _build_popular_model(self):
        model_path = self._get_model_path()
        if os.path.isfile(model_path):
            return

        freq = {}
        print('building model..')
        for path, _ in tqdm.tqdm(
                iterate_data_files(self.from_dtm,
                                   self.to_dtm),  # 이 기간 사이에 많이 읽힌 글을 추천
                mininterval=1):
            for line in open(path):
                seen = line.strip().split()[
                    1:]  # read에서 읽어온 한 줄 한줄을 쪼개고 뒷부분이 읽은글리스트
                for s in seen:  # 줄마다 읽은글리스트의 읽은글들에 대해
                    freq[s] = freq.get(
                        s, 0
                    ) + 1  #  freq이라는 dictionary의 읽은글의 frequency값을 가져오고 없으면 0을 가져옴(default 0),그리고 1더해줌
        freq = sorted(freq.items(), key=lambda x: x[1],
                      reverse=True)  # freq을 정렬할 때 reverse로..
        open(model_path, 'wb').write(cPickle.dumps(freq, 2))
        print('model built')
コード例 #14
0
    def _build_model(self):
        """_build_model은 사용자에게 유저를 추천하는 모델을 만드는 함수입니다.
        사용자에게 추천되는 기준은 빈번하게 열람당하는 유저입니다.
        """
        #model_path은 str 클래스 입니다. 모델이 위치한 경로를 갖습니다.
        model_path = self._get_model_path()
        #만약 모델이 있다면 build 과정을 멈춘다.
        if os.path.isfile(model_path):
            return

        #freq는 딕셔너리입니다.
        #"사용자가 열람한 유저"를 키로 갖습니다. 그 유저의 등장횟수를 값으로 갖습니다.
        freq = {}
        print('building model..')
        #iterate_data_files의 반환값은 from_dtm과 to_dtm 사이에 있는 (파일의 경로와, 파일 이름)입니다.
        #이 반복문은 파일의 경로만 받고, 파일의 이름은 _로 무시합니다.
        for path, _ in tqdm.tqdm(iterate_data_files(self.from_dtm,
                                                    self.to_dtm),
                                 mininterval=1):
            #경로에 있는 파일을 열어서 모든 line을 차례대로 읽습니다.
            #line은 (사용자의 해시값) (@사용자가 열람한 유저들) 로 이뤄진 하나의 문자열입니다.
            for line in open(path):
                #seen은 (@사용자가 열람한 유저들)로 이뤄진 list입니다.
                seen = line.strip().split()[1:]
                for s in seen:
                    #s는 seen list에 나타나는 한 (@유저) 입니다.
                    #s는 freq에서 '키'로 사용됩니다.
                    #freq.get(s, 0) + 1을 통해 s가 등장할때 마다 빈도수가 늘어납니다.
                    freq[s] = freq.get(s, 0) + 1

        #freq.items()는 ['유저', 빈도 ] 로 구성된 리스트입니다.
        #lambda x: x[1]은 freq.items()[1]을 조회합니다. 이는 곧 빈도를 조회합니다.
        #reverse가 True이기 때문에 내림차순으로 정렬합니다. 이는 첫 원소가 빈도수가 높은 유저임을 의미합니다.
        freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)

        #모델의 경로에 freq내용을 직렬화 하여 저장합니다.
        open(model_path, 'wb').write(cPickle.dumps(freq, 2))
        print('model built')
コード例 #15
0
def read_reads():
    print("read reads of all users")
    files = sorted(
        [path for path, _ in iterate_data_files('2018100100', '2019030100')])
    for path in tqdm.tqdm(files, mininterval=1):
        date = path[11:19]
        for line in open(path):
            tokens = line.strip().split()
            user_id = tokens[0]
            reads = tokens[1:]
            if len(reads) < 1: continue
            if user_id in t_users:  # 예측 대상 사용자들
                if user_id in t_reads:  # 예측 대상 사용자들의 조회인 경우 t_reads에 저장
                    t_reads[user_id] += reads
                    # 동일한 사용자가 서로 다른 시간에 접속하여 조회한 경우 조회한 글을 t_reads의 하나의 키에 시간의 순서를 유지하면서 모아서 저장
                else:
                    t_reads[user_id] = reads

                # 테스트 데이터랑 중첩된 날짜에 대해선 t_reads_dup에 저장
                if date >= "20190222":
                    if user_id in t_reads_dup:
                        t_reads_dup[user_id] += reads
                    else:
                        t_reads_dup[user_id] = reads

                reads_set = set(reads)  # 같은 세션 내에서 여러번 조회한 경우 중복을 제거하기 위함
                for read in reads_set:
                    writer = read.split("_")[0]
                    # t_followings: 각 유저의 구독 작가 목록 딕셔너리
                    if (user_id not in t_followings) or (
                            writer not in t_followings[user_id]):
                        if user_id in t_non_follows:
                            if writer in t_non_follows[
                                    user_id]:  # 구독 관계가 없는 작가에 대해 읽은 횟수를 저장 (키=독자아이디/작가아이디, 값=읽은 횟수)
                                t_non_follows[user_id][writer] += 1
                            else:
                                t_non_follows[user_id][writer] = 1
                        else:
                            t_non_follows[user_id] = {}
                            t_non_follows[user_id][writer] = 1

            num_reads_n1 = len(reads) - 1

            # 바로 다음 글 조회 기록 저장
            for i, read in enumerate(reads):
                if i < num_reads_n1:
                    if read == reads[i + 1]:
                        continue  # when two continous reads are the same (바로 다음글이 같은 글이면 continue)
                    if read in seq_reads:
                        if reads[i + 1] in seq_reads[read]:
                            seq_reads[read][reads[
                                i +
                                1]] += 1  # seq_reads: 연속 조회 통계 저장 (바로 다음 조회한 글의 아이디를 저장!!)
                        else:
                            seq_reads[read][reads[i + 1]] = 1
                    else:
                        seq_reads[read] = {}
                        seq_reads[read][reads[i + 1]] = 1

            # 바로 이전 글 조회 기록 저장
            for i, read in enumerate(reads):
                if i < num_reads_n1:
                    nread = reads[i + 1]
                    if read == nread:
                        continue  # when two continous reads are the same
                    if nread in prev_reads:
                        if read in prev_reads[nread]:
                            prev_reads[nread][read] += 1
                        else:
                            prev_reads[nread][read] = 1
                    else:
                        prev_reads[nread] = {}
                        prev_reads[nread][read] = 1

    # 예측 대상 사용자들이 중첩 기간에 조회한 글이 하나도 없는 경우, 가장 최근에 읽은 글 10개를 t_reads_dup에 저장
    for user in t_reads:
        if user not in t_reads_dup:
            t_reads_dup[user] = t_reads[user][
                -10:]  # t_reads_dup: 예측 대상 사용자들의 글 조회
コード例 #16
0
ファイル: inference.py プロジェクト: you90100/brunch_nafma
def read_reads():
    print("read reads of all users")
    files = sorted(
        [path for path, _ in iterate_data_files('2018100100', '2019030100')])
    for path in tqdm.tqdm(files, mininterval=1):
        date = path[11:19]
        for line in open(path):
            tokens = line.strip().split()
            user_id = tokens[0]
            reads = tokens[1:]
            if len(reads) < 1: continue
            if user_id in t_users:
                if user_id in t_reads:
                    t_reads[user_id] += reads
                else:
                    t_reads[user_id] = reads
                if date >= "20190222":
                    if user_id in t_reads_dup:
                        t_reads_dup[user_id] += reads
                    else:
                        t_reads_dup[user_id] = reads

                reads_set = set(reads)
                for read in reads_set:
                    writer = read.split("_")[0]
                    if (user_id not in t_followings) or (
                            writer not in t_followings[user_id]):
                        if user_id in t_non_follows:
                            if writer in t_non_follows[user_id]:
                                t_non_follows[user_id][writer] += 1
                            else:
                                t_non_follows[user_id][writer] = 1
                        else:
                            t_non_follows[user_id] = {}
                            t_non_follows[user_id][writer] = 1

            num_reads_n1 = len(reads) - 1
            for i, read in enumerate(reads):
                if i < num_reads_n1:
                    if read == reads[i + 1]:
                        continue  # when two continous reads are the same
                    if read in seq_reads:
                        if reads[i + 1] in seq_reads[read]:
                            seq_reads[read][reads[i + 1]] += 1
                        else:
                            seq_reads[read][reads[i + 1]] = 1
                    else:
                        seq_reads[read] = {}
                        seq_reads[read][reads[i + 1]] = 1

            for i, read in enumerate(reads):
                if i < num_reads_n1:
                    nread = reads[i + 1]
                    if read == nread:
                        continue  # when two continous reads are the same
                    if nread in prev_reads:
                        if read in prev_reads[nread]:
                            prev_reads[nread][read] += 1
                        else:
                            prev_reads[nread][read] = 1
                    else:
                        prev_reads[nread] = {}
                        prev_reads[nread][read] = 1

    for user in t_reads:
        if user not in t_reads_dup:
            t_reads_dup[user] = t_reads[user][-10:]
コード例 #17
0
def main():
    print("Start inference!")
    test_end_date = "20190314"
    """
    아래 파라미터는 실험에 의해 적합한 값을 고름
    test_days_len: 테스트 날짜로부터 이후 날짜 수 ex) 20
    additional_days_len: 테스트 날짜로부터 이전 날짜 수 ex) 4

    테스트 시작 날짜로 부터 <앞뒤> 기간에 쓰여진 문서를 candidate doc 으로 사용
    """
    test_days_len = 20
    additional_days_len = 4
    candidates_len = test_days_len + additional_days_len

    users_dict = {}
    with codecs.open('./res/users.json', 'rU', 'utf-8') as f:
        for line in f:
            j_map = json.loads(line)
            users_dict[j_map['id']] = j_map

    cand_docs = {}

    t_obj = datetime.strptime(test_end_date, "%Y%m%d")
    doc_deadline_date = (t_obj + timedelta(days=1)).strftime("%Y%m%d")
    candidate_date = (t_obj -
                      timedelta(days=candidates_len)).strftime("%Y%m%d")

    doc_deadline_date = int(doc_deadline_date) * 100
    candidate_date = int(candidate_date) * 100

    with codecs.open('./res/metadata.json', 'rU', 'utf-8') as f:
        for line in f:
            j_map = json.loads(line)

            # ts 를 datetime 으로 변경
            j_map['time'] = ts2time(j_map['reg_ts'])

            # [test 기간 + test 이전 몇 일 기간] 동안의 doc 정보 저장
            if j_map['time'] < doc_deadline_date and j_map[
                    'time'] > candidate_date:
                cand_docs[j_map['id']] = j_map

    print("# of candidate articles from {} to {} : {}".format(
        candidate_date // 100, test_end_date, len(cand_docs)))

    # 20190221 부터 한 달간의 클릭 문서 분포를 파악
    d_obj = datetime.strptime("20190221", "%Y%m%d")
    date_list = []
    for i in range(30):
        date_list.append((d_obj - timedelta(days=i)).strftime("%Y%m%d"))

    dist_map = get_click_dist(date_list, test_days_len, additional_days_len)

    s_obj = datetime.strptime("20190222", "%Y%m%d")

    dist_sorted_map = sorted(dist_map.items(), key=lambda k: -k[1])
    click_rank_per_date = [((s_obj + timedelta(days=e[0])).strftime("%Y%m%d"),
                            rank) for rank, e in enumerate(dist_sorted_map)]
    click_rank_per_date = dict(click_rank_per_date)
    print(click_rank_per_date)

    # 후보 doc 들을 writer 로 묶어줌
    cand_doc_writer = {}
    for doc_id, doc_info in cand_docs.items():
        writer = doc_info['user_id']
        cand_doc_writer[writer] = cand_doc_writer.get(writer, []) + [doc_id]

    for k, v in cand_doc_writer.items():
        c_v = [(e, int(e.split("_")[1])) for e in v]
        cand_doc_writer[k] = [(e[0], int(cand_docs[e[0]]['time']))
                              for e in sorted(c_v, key=lambda v: v[1])]

    user_seen = {}
    user_latest_seen = {}
    user_last_seen = {}

    # w2v 에 쓰일 sequences
    seen_seq = []
    all_articles = []

    # test 의 (겹치는)기간 동안의 doc 사용량
    doc_cnt = {}

    from_dtm = 2018100100
    to_dtm = 2019030100
    for path, _ in tqdm(iterate_data_files(from_dtm, to_dtm), mininterval=1):
        for line in open(path):
            l = line.strip().split()
            user = l[0]
            seen = l[1:]

            if len(seen) > 1:
                seen_seq.append(seen)
            all_articles += seen

            user_seen[user] = user_seen.get(user, []) + seen

            date_range = path.split("./res/read/")[1]
            fr = int(date_range.split("_")[0])

            if fr >= 2019020100:
                user_latest_seen[user] = user_latest_seen.get(user, []) + seen

            if fr < 2019022200:
                user_last_seen[user] = user_last_seen.get(user, []) + [fr]

            if fr >= 2019022200:
                for doc in seen:
                    doc_cnt[doc] = doc_cnt.get(doc, 0) + 1

    for u, dates in user_last_seen.items():
        user_last_seen[u] = max(dates)

    doc_cnt = OrderedDict(sorted(doc_cnt.items(), key=lambda k: -k[1]))
    pop_list = [k for k, v in doc_cnt.items()][:300]
    del doc_cnt

    # word2vec 에 이용하는 데이터 만들기
    vocabulary_size = len(set(all_articles))
    _, _, article2idx_map, idx2article_map = \
        build_dataset(all_articles, seen_seq.copy(), vocabulary_size, min_count=5, skip_window=4)
    filtered_vocabulary_size = len(article2idx_map)

    del all_articles
    del seen_seq

    print("# of vocabulary : all ({}) -> filtered ({})".format(
        vocabulary_size, filtered_vocabulary_size))

    batch_size = 128
    embedding_size = 128
    num_sampled = 10

    config = {}
    config['batch_size'] = batch_size
    config['embedding_size'] = embedding_size
    config['num_sampled'] = num_sampled
    config['filtered_vocaulary_size'] = filtered_vocabulary_size

    # word2vec ckpt 불러오기
    sess = tf.Session()
    net = word2vec(sess, config)
    net.build_model()
    net.initialize_variables()
    net.restore_from_checkpoint(ckpt_path="./ckpt/",
                                step=500000,
                                use_latest=True)

    user_most_seen = {}
    for u, seen in user_latest_seen.items():
        for doc in seen:
            if doc.startswith("@"):
                writer = doc.split("_")[0]
                seen_map = user_most_seen.get(u, {})
                seen_map[writer] = seen_map.get(writer, 0) + 1
                user_most_seen[u] = seen_map

        if u in user_most_seen:
            user_most_seen[u] = dict([
                e
                for e in sorted(user_most_seen[u].items(), key=lambda k: -k[1])
            ])

    #tmp_dev = ['./tmp/dev.users.recommend', './tmp/dev.users']
    #dev = ['./res/predict/dev.recommend.txt', './res/predict/dev.users']
    test = ['./res/predict/recommend.txt', './res/predict/test.users']

    path_list = [test]
    for output_path, user_path in path_list:

        print("Start recommendation!")
        print("Read data from {}".format(user_path))
        print("Write data to {}".format(output_path))

        ## word2vec 에 의한 top_n 먼저 계산
        articles_len = 4
        positives = []
        with codecs.open(user_path, mode='r') as f:
            for idx, line in enumerate(f):
                u = line.rsplit()[0]

                pos = [
                    article2idx_map[e] for e in reversed(user_seen.get(u, []))
                    if e in article2idx_map
                ][:articles_len]
                remain_len = articles_len - len(pos)
                pos += [filtered_vocabulary_size for _ in range(remain_len)]
                positives.append(np.array(pos))

        _, _, top_n_bests = net.most_similar(positives,
                                             idx2article_map=idx2article_map,
                                             top_n=300)

        top_n_bests = np.array(top_n_bests)[:, :, 0]

        with codecs.open(output_path, mode='w') as w_f:
            with codecs.open(user_path, mode='r') as f:
                for idx, line in tqdm(enumerate(f)):
                    u = line.rsplit()[0]

                    user_most_seen_map = user_most_seen.get(u, {})

                    def rerank_doc(doc_list):
                        """
                        rerank : 세 가지 방식으로 doc_list 로 들어온 문서들을 재정렬함
                        - 우선순위 1. 유저가 과거(user_latest_seen) 에 본 에디터의 글 횟 수 -> 많을수록 우선
                        - 우선순위 2. 해당 날짜에 만들어진 문서가 클릭될 확률 순위(click_rank_per_date) -> rank 작을 수록 우선
                        - 우선순위 3. 문서가 만들어진 최신 순

                        """
                        n_doc_list = []
                        for e in doc_list:
                            if e[1] > user_last_seen.get(u, 0) and str(
                                    e[1] // 100) in click_rank_per_date:
                                writer = e[0].split("_")[0]
                                writer_hit_cnt = user_most_seen_map.get(
                                    writer, 0)
                                n_doc_list.append(
                                    (e[0], e[1],
                                     click_rank_per_date[str(e[1] // 100)],
                                     writer_hit_cnt))

                        reranked_doc_list = [
                            e[0]
                            for e in sorted(n_doc_list,
                                            key=lambda k: (-k[3], k[2], k[1]))
                        ]
                        return reranked_doc_list

                    ### 추천은 아래 1 + 2 + 3 순서로 함

                    # 1. 구독한 에디터들의 글 중들을 candidate 에서 뽑기
                    following_list = users_dict.get(
                        u, {'following_list': []})['following_list']
                    following_doc = []
                    if following_list:
                        for e in following_list:
                            following_doc += cand_doc_writer.get(e, [])
                        following_doc = rerank_doc(following_doc)

                    # 2. 유저가 많이 본 에디터의 글들을 candidate 에서 뽑기
                    most_seen_new_doc = []
                    if user_most_seen_map:
                        for e, writer_cnt in user_most_seen_map.items():
                            # writer 가 3 번 이상 본 경우에만 활용
                            if writer_cnt >= 3:
                                most_seen_new_doc += cand_doc_writer.get(e, [])
                        most_seen_new_doc = rerank_doc(most_seen_new_doc)

                    # 3. word2vec 모델에서 가장 최근에 본 n 개 문서와 가장 유사한 문서들을 뽑기
                    positive_input = [
                        article2idx_map[e]
                        for e in reversed(user_seen.get(u, []))
                        if e in article2idx_map
                    ][:articles_len]
                    if positive_input:
                        sim_list = list(top_n_bests[idx])
                    else:
                        sim_list = pop_list

                    # 최종 추천 (1 + 2 + 3)
                    rec_docs = following_doc + most_seen_new_doc + sim_list
                    rec_docs = list(OrderedDict.fromkeys(rec_docs))

                    # 이미 유저가 과거에 본 문서는 제거
                    n_rec_docs = []
                    for d in rec_docs:
                        if d not in user_seen.get(u, []):
                            n_rec_docs.append(d)

                    if len(n_rec_docs) < 100:
                        n_rec_docs = pop_list

                    line = "{} {}\n".format(u, ' '.join(n_rec_docs[:100]))
                    w_f.write(line)
        print("Finish!")
コード例 #18
0
def train(args):
    from_dtm = 2018100100
    to_dtm = 2019030100

    all_articles = []
    seen_seq = []

    for path, _ in tqdm(iterate_data_files(from_dtm, to_dtm), mininterval=1):
        for line in open(path):
            l = line.strip().split()
            user = l[0]
            seen = l[1:]

            if len(seen) > 1:
                seen_seq.append(seen)
            all_articles += seen

    vocabulary_size = len(set(all_articles))

    new_seen, count, article2idx_map, idx2article_map = \
        build_dataset(all_articles, seen_seq.copy(), vocabulary_size, min_count=args.min_count, skip_window=args.skip_window)

    filtered_vocabulary_size = len(article2idx_map)

    print('Most common words', count[:5])
    print("# of sentences : all ({}) -> filtered ({})".format(
        len(seen_seq), len(new_seen)))
    print("# of vocabulary : all ({}) -> filtered ({})".format(
        vocabulary_size, filtered_vocabulary_size))

    # Reduce momory
    del all_articles
    del seen_seq

    span = 2 * args.skip_window + 1  # [ skip_window target skip_window ]
    buffer = deque(maxlen=span)  # pylint: disable=redefined-builtin
    skip_dummy = ['UNK'] * args.skip_window

    all_targets = []
    all_labels = []

    for sen_idx, sentence in tqdm(enumerate(new_seen), total=len(new_seen)):
        sentence = skip_dummy + sentence + skip_dummy
        buffer.extend(sentence[0:span - 1])

        for doc in sentence[span - 1:]:
            buffer.append(doc)
            if buffer[args.skip_window] != 'UNK':
                context_words = [
                    w for w in range(span)
                    if w != args.skip_window and buffer[w] != 'UNK'
                ]
                _num_sample = len(context_words) if len(
                    context_words) < args.num_skips else args.num_skips
                words_to_use = random.sample(context_words, _num_sample)

                for j, context_word in enumerate(words_to_use):
                    all_targets.append(
                        article2idx_map[buffer[args.skip_window]])
                    all_labels.append(article2idx_map[buffer[context_word]])

    t1 = time()
    print("Shuffling indexes...")
    idxes = [e for e in range(len(all_targets))]
    random.shuffle(idxes)
    all_targets = np.array(all_targets)[idxes]
    all_labels = np.array(all_labels)[idxes]
    del idxes
    t2 = time()
    print("Shuffling finished [{:.1f} s]".format(t2 - t1))

    config = {}
    config['batch_size'] = args.batch_size
    config['embedding_size'] = args.embedding_size
    config['skip_window'] = args.skip_window
    config['num_skips'] = args.num_skips
    config['num_sampled'] = args.num_sampled
    config['filtered_vocaulary_size'] = filtered_vocabulary_size

    sess = tf.Session()
    net = word2vec(sess, config)
    net.build_model()
    net.initialize_variables()

    decay_alpha = (args.alpha - args.min_alpha) / args.num_steps
    alpha = args.alpha

    check_step = 10000
    save_step = 100000
    average_loss = 0
    t1 = time()
    for step in range(args.num_steps):
        batch_inputs, batch_labels = generate_batch(args.batch_size,
                                                    all_targets, all_labels)

        loss_val = net.train(batch_inputs, batch_labels, alpha=alpha)
        alpha -= decay_alpha
        average_loss += loss_val

        if step % check_step == 0 and step > 0:
            average_loss /= check_step

            t2 = time()
            print("Average loss at step {}: {:.5} [{:.1f} s]".format(
                step, average_loss, t2 - t1))
            t1 = t2
            average_loss = 0

        if (step % save_step == 0 and step > 0) or step + 1 == args.num_steps:
            print("Store checkpoints at step {}...".format(step))
            net.store_checkpoint(step=step)
コード例 #19
0
import os
import random

import six
import fire
import mmh3
import tqdm

from util import iterate_data_files


def groupby(from_dtm, to_dtm, tm    p_dir, out_path, num_chunks=10):
    from_dtm, to_dtm = map(str, [from_dtm, to_dtm])
    fouts = {idx: open(os.path.join(tmp_dir, str(idx)), 'w')
             for idx in range(num_chunks)}
    files = sorted([path for path, _ in iterate_data_files(from_dtm, to_dtm)])
    for path in tqdm.tqdm(files, mininterval=1):
        for line in open(path):
            user = line.strip().split()[0]
            chunk_index = mmh3.hash(user, 17) % num_chunks
            fouts[chunk_index].write(line)

    map(lambda x: x.close(), fouts.values())
    with open(out_path, 'w') as fout:
        for chunk_idx in fouts.keys():
            _groupby = {}
            chunk_path = os.path.join(tmp_dir, str(chunk_idx))
            for line in open(chunk_path):
                tkns = line.strip().split()
                userid, seen = tkns[0], tkns[1:]
                _groupby.setdefault(userid, []).extend(seen)