【학습 강화】cpprb로 변환된 파일 저장 기능 추가

이쪽 Scrap으로 조사·토론하는 기능을 게시해 기사화했기 때문이다.
https://zenn.dev/ymd_h/scraps/28a2d2ed39d49f

1. 계기


https://github.com/ymd-h/cpprb/discussions/10
off-policy의 강화 학습에서 Replay Buffer에 잠시 저장된 전환은t(\text{상태}), ~at(\text\행동}), ~rt(\text{보수}), ~s{t+1}(\text{조치 후 상태}), ~dt(\text{스토리 종료 여부)\rbrace의 그룹, st와 s{t+1}이 포함되어 있기 때문에 이중으로 저장하기 쉽습니다.
개인이 개발한cpprb에서 내부 데이터를 저장함으로써 메모리 사용량을 줄이는 기능을 선택할 수 있다. 예를 들어 메모리의 같은 위치를 참조하는 것이다.
(이 기능에 관한 기사는 두 달 전부터 다 쓰지 못한 초고가 잊혀져 있었다는 것을 발견했다.)
내부에 저장된 변환을 꺼낼 때 (일반적으로) 무작위로 꺼냈기 때문에 각각 독립된 데이터로 꺼냅니다.따라서 모든 데이터를 추출하여 파일에 저장하는 전략을 취하면 st와 s{t+1}은 이중으로 저장됩니다.고차원 상태의 문제를 처리할 때 이 데이터는 매우 커지기 때문에 중복 저장을 중지함으로써 파일 크기를 줄일 수 있다.
이런 상황에서'파일 크기를 줄이고자 한다'는 사용자 피드백을 계기로 이번 기능 개발에 힘쓰고 있다.

2. 조사


2.1 원래 필요한가요?


파일 저장 라이브러리의 대부분은 압축된 것이기 때문에 실제로는 번거로운 일을 하지 않아도 압축 알고리즘은 중복된 데이터를 배제할 수 있다.
파일 크기가 작을 때는 제외했지만 커지면 반복 영역을 찾을 수 없습니다.
https://github.com/ymd-h/cpprb/discussions/10#discussioncomment-493892
https://github.com/ymd-h/cpprb/discussions/10#discussioncomment-496073

2.2 사용 가능한 라이브러리 (또는 함수)


  • pickle
  • 파이톤의 표준 라이브러리에서 데이터의 직접화(즉 파일을 저장할 수 있는 형식으로 변환)를 진행한다.(기타 라이브러리도 대부분 내부 사용pickle
  • 압축 기능은 없지만 다른 라이브러리(표준 라이브러리 포함)에서 구현

  • joblib.dump
  • 여러 압축 알고리즘을 사용하여 사용이 편리하고 성능이 좋다
  • TensorFolow 등 다른 라이브러리에서도 사용하기 때문에 의존관계의 복잡화

  • numpy.savez_compressed
  • 여러 객체ndarray 통합 저장/압축
  • dtype=objectndarray도 (내부용pickle으로
  • 사용 가능

  • PyArrow
  • 열 형식의 Parquet 형식을 처리하는 Apache Arrow의 Python 바인딩

  • fastparquet
  • parquet 형식의 라이브러리를 똑같이 처리합니다.
  • 병행 분석 라이브러리의 dask 개발
  • https://zenn.dev/ymd_h/scraps/28a2d2ed39d49f#comment-732f04c85880d9
    joblib 설치를 진행했지만 새로운 의존 프로그램 라이브러리를 추가해야 하기 때문에 중도에 방침을 바꿨습니다, numby.savez_comporessed를 사용하기로 결정했습니다.

    3. 사용법


    공식 문서

    3.1 API


    구현은 ReplayBuffer.save_transitions(self, file, *, safe=True)ReplayBuffer.load_transitions(self, file)입니다.
    기본적으로 safe=True 변환은 저장을 잠시 꺼냅니다.사용자가 수동으로 safe=False을 지정할 때부터 위에서 설명한 중복 제거 상태로 데이터를 저장합니다.이 압축은 내부 구조에 의존하기 때문에 (가능한 한 변환 등으로 대응) 장래에 변경될 때 영향을 받을 수 있다.safe=True라면 최악의 사용자는 수동으로 안의 데이터를 검색할 수 있다.
    https://zenn.dev/ymd_h/scraps/28a2d2ed39d49f#comment-c113384b267ee2

    3.2 Example


    from cpprb import ReplayBuffer
    
    rb1 = ReplayBuffer(256,
                       {"obs": {"shape": 3}, "act": {},
                        "rew": {}, "done": {}},
                       next_of="obs")
    
    # 保存する遷移を詰める。
    # (本当は、エージェントを行動させて得た遷移をですが、ここでは適当に)
    rb1.add(obs=[1, 2, 3], act=1, rew=1, next_obs=[2, 3, 4], done=0)
    
    # ファイル拡張子は、 ".npz" (違うと勝手に付け足されます。)
    rb1.save_transitions("transitions.npz")
    
    # 通常はここでプログラムが終わって、別のプログラムで読み出す。
    
    # 新しく Replay Buffer を(互換性のある構成で)作る。
    rb2 = ReplayBuffer(256,
                       {"obs": {"shape": 3}, "act": {},
                        "rew": {}, "done": {}},
                       next_of="obs")
    
    # ファイルを読み込む。(上書きではなく、追加。)
    rb2.load_transitions("transitions.npz")
    

    4. 잡담


    4.1 사실...


    사실 오래전부터 파일 저장 기능을 요구하는 사람이 있었다.
    https://gitlab.com/ymd_h/cpprb/-/issues/68
    하지만 그럴 수 없어 리플레이 버퍼에서 변환을 꺼내 스스로 저장했다.
    (당시 모든 데이터get_all_transitions(self)를 꺼내지 않았기 때문에 내부 API의_encode_sample(self, idx)를 사용했는데 대답이 좀 미묘했다.)
    파일에 데이터를 저장하려면 프로그램 가동 기간의 통합성뿐만 아니라 미래의 호환성도 고려해야 하기 때문에 다른 방법으로 실현할 수 있다면 제공하기 어렵다는 것이다.
    이번에는 단순 편의성뿐만 아니라 제공하지 않으면 중복 배제가 불가능하기 때문에 역도 조치를 취했다.(이 기능이 향후 개선에 걸림돌이 되지 않을 것으로 기대한다.)

    4.2 빈대...


    기능 개발 과정에서 기준 비교 대상Ray/RLlib의 오류가 발생하여 issue를 보고했습니다.멤버 변수 이름 Refactoring으로 간주되는 동안 오류가 발생했습니다.(Proritized에 문제가 없기 때문에 사용되지 않으면 무시되는 거죠.)
    https://github.com/ray-project/ray/issues/14818

    4.3 테스트


    이번엔 1개 기능의 구현이지만 영향이 크기 때문에 테스트용 파일을 다시 만들려고 평소보다 많은 테스트를 썼는데, 아이고.
    https://gitlab.com/ymd_h/cpprb/-/blob/02b3469a7be1d060150d18529f3df029c8946ea4/test/save_load.py

    좋은 웹페이지 즐겨찾기