ONEforALL-S003 / ONE

On-device Neural Engine / q-implant
Other
1 stars 2 forks source link

Q-Implant Output Circle Node & Not Operator Node(Maybe Activation) #1

Open ssafyWooJeong opened 1 year ago

ssafyWooJeong commented 1 year ago

Torch 상에서 Tensor를 다 Mapping 하여 Quantization Parameter를 추출한 이후 Q-Implant를 시도하였을 때, image

Circle Output Node를 처리해주는 부분에서, Output의 직전 Node를 참조하여 Quantization Parameter를 Output Circle의 Dequantize에 필요한 Quantization Parameter를 사용하는 것이라 생각되어 지는데,

torch/onnx circle
torch/onnx circle

Torch → Circle 과정에서 Transpose와 같이 Quantization Parameter를 갖지 않는 Operator가 추가되어 변환 되었을 때 직전 Node가 Quantization Parameter를 갖고 있다는 것이 보장 되지 않는 것 같습니다.

Commit 과 같이 직전 Node에 qparam이 없는 경우 해당 노드 이전을 따라가며 qparam을 찾아서 해결할 수 있을 것 같은데 어떻게 생각하시는지 궁금합니다.

image 해당 부분을 적용하였을 때도, Activation이나 Transpose 같은 Node의 data type이 float로 남아 문제가 되는 것 같은데 Q-implant 적용 될 때 해당 Node의 Predecessor를 참고하여 따라가다 보면 사용해야하는 Quantization Parameter를 얻을 수 있을 것 같은데, 어떻게 생각하시는지 궁금합니다. (Circle Constant의 경우 Successor가 없을 것이라고 생각되고, Circle Node를 따라가면 사용해야 할 Quantization Parameter를 얻을 수 있을 것이라 생각합니다.)

추가적으로 PyTorch의 Quantization Parameter를 추출하는 과정에서, One 내의 PyTorch Example과 Version을 맞춰야한다 생각하여 PyTorch 1.7.0에 맞추어주었는데, PyTorch 1.7.0에는 quantized Activation이 몇 개 존재하지 않아, Model 변환만 onnx_tf 사용 때문에 PyTorch 1.7.0에서 변환해주는 것이고, Model Train/Inference는 Latest에서 이루어지는 것이라 가정하여 Quantization Parameter 추출도 Latest에서 이루어져야 한다 가정해야 하나 궁금합니다.

PyTorch 1.7.0 Activation PyTorch Latest Activation
Github Github

PyTorch Quantization Parameter Export Draft Torch ↔ Circle 간의 Name 차이로 인한 Mapping의 필요성을 Torch에서의 Tensor 값과 Circle에서의 Tensor 값을 기반으로 해결해주었는데(이 과정에서 onnx_tf, tf, tflite2circle, pics를 필요로 합니다.), 이를 Torch ↔ Circle 간의 Mapping을 진행해서 json 같은 형식으로 저장해주는 클래스와, Torch에서 Quantization Parameter를 추출하고 (Mapping에 대한 정보가 제공된다면 Mapping까지 적용하여) 저장해주는 클래스로 구분하여 각 실행 환경을 Torch 1.7.0 과 Latest로 구분해서 사용하는 걸 가정하면 되겠다.라는 생각이 드는데 맞게 생각하고 있나 궁금합니다.

jinevening commented 1 year ago

질문이 조금 많은 것 같은데, 1, 2, 3 같이 정리해주면 읽기 쉬울 것 같습니다 :)

직전 Node에 qparam이 없는 경우 해당 노드 이전을 따라가며 qparam을 찾아서 해결할 수 있을 것 같은데 어떻게 생각하시는지 궁금합니다.

좋은 시도 입니다. 만약 predecessor 노드의 input/output 의 qparam 이 같다면 working 할 것 같습니다 (ex: Transpose). 다만 그런 Op 가 아니라면 working 하기 어렵기 때문에 그에 대한 체크가 있어야 합니다.

Quantization Parameter 추출도 Latest에서 이루어져야 한다 가정해야 하나 궁금합니다.

ONE 안에서 torch 의 qparam 을 추출하려고 한다면 ONE 의 torch 버전에 맞추어야 합니다. 많은 레이어를 지원하지 않아도 괜찮습니다.

Torch ↔ Circle 간의 Name 차이로 인한 Mapping의 필요성을 Torch에서의 Tensor 값과 Circle에서의 Tensor 값을 기반으로 해결해주었는데

Tensor 값을 기반으로 해결하는 방식이 어느정도 general 한 것인지 확인이 필요합니다. 그런데 지금 개발 중이신 모듈이 무엇을 하는 것인지 제가 잘 이해가 안돼서, 구체적으로 개발 중이신 모듈의 역할 (i.e., input/output 이 무엇인지) 에 대해 설명해주시면 좋을 것 같습니다.

ssafyWooJeong commented 1 year ago

@jinevening 답변 감사드립니다...! 다음 질문부터는 리스트로 구분하여 질문 드리도록 하겠습니다!

  1. luci::CircleOpcode을 기준으로 Transpose, Reshape, Split 과 같은 Operation 인지 확인하는 것에 대해 고민 중이었는데, 추가적으로 OpCode 확인 및 Input과 Output의 qparam을 체크하도록 하겠습니다..!
  2. Quantization이 이루어지는 환경도 PyTorch 1.7.0 으로 이해하고, Softmax, Sigmoid와 같은 Activation 지원에 대해 고민할 필요 없다는 것으로 이해하겠습니다..!
  3. 필요한 부분을 그때 그때 추가하다 보니 구조화된 코드가 아니여서 죄송합니다..ㅠㅠ 돌아가는 걸 확인 후 코드 정리하여 Draft로 올리려고 생각 중이었는데, 코드 구조화를 먼저 진행하도록 하겠습니다..! Tensor 값을 기준으로 매핑하는 방식 자체는 모든 Tensor의 값이 Unique 하다는 가정 하에, Torch에 있는 Tensor 값과 같은 값을 가지고 있는 Circle의 Tensor를 찾아서 두 이름을 매핑하는 방식입니다. (Tensor의 Numpy를 binary data로 읽어서 hashing을 통해 Mapping을 진행하였습니다. numpy의 binary data에 dimension 정보와 값에 대한 정보를 담고 있어서 정확하게 동일한 Tensor가 존재하지 않는 이상 충돌은 나지 않을 것으로 생각합니다.)

해당 스크립트 자체는 아래와 같은 과정을 거칩니다.

감사합니다.

jinevening commented 1 year ago

상세한 설명 감사합니다. 제가 이해하기로는 스크립트의 input 은 nn.Module 이 구현된 Torch code 이고, output 은 qparam.json 인 것 같습니다. 만약 그렇다면 Torch code 의 파일 위치와, qparam.json 이 저장될 위치를 argument 로 받는 형태를 가지면 좋을 것 같습니다.

모든 Tensor의 값이 Unique 하다는 가정 하에

이 부분이 염려스럽네요. 0 이나 1, pre-defined filter 를 사용하는 Conv 같은 경우에는 같은 값을 가지는 tensor 들이 있을 것 같아서요. 만약 중복 값을 가지는 tensor 들이 있다면 exception 을 던지는 체크가 있어야 할 것 같습니다.

그리고, 값이 없는 tensor (ex: activation) 같은 경우에는 어떤식으로 매칭을 하는건가요?

ssafyWooJeong commented 1 year ago

스크립트의 input 은 nn.Module 이 구현된 Torch code 이고, output 은 qparam.json 인 것 같습니다.

말씀해주신 Input으로 들어오는 nn.Module이 구현 된 Torch Code라는 것이 nn.Module을 상속 받아 구현한 Network라고 생각되고, 양자화 되지 않은 원본 모델이라 생각 됩니다..!

PyTorch 상에서 qparam을 뽑는 목적이 circle에 적용 되어 있지 않고, PyTorch로 구현 된 Quantization Algorithm을 적용하기 위한 것이라 생각하여 기본적으로 스크립트에 들어와야 하는 input은 PyTorch Framework 상에서 양자화된 Module이라고 생각했고, 추가적으로 매핑하기 위해 원본 모델을 input으로 받아 사용하고 있습니다.

Program Argument로 원본 Module을 받는 경우 스크립트 내에서 양자화할 수 있는 방법은 Traditional한 Built in Quantization Option 밖에 없을 것 같은데, 그 방법이 맞는 건지 궁금합니다..!

해당 방법이 맞다고 하시면, 만들어둔 Helper Class 를 이용하여 original torch code를 받아서 양자화 및 qparam을 만드는 main 문을 만들면 될 것 같습니다.

이 부분이 염려스럽네요. 0 이나 1, pre-defined filter 를 사용하는 Conv 같은 경우에는 같은 값을 가지는 tensor 들이 있을 것 같아서요. 만약 중복 값을 가지는 tensor 들이 있다면 exception 을 던지는 체크가 있어야 할 것 같습니다.

Exception Retry

중복되는 Tensor 값이 존재하는 경우 Exception을 발생 시키고, 원본 모델의 Tensor 값을 변경한 후 다시 매핑을 시도하는 방식도 가능할 것 같은데 이 방법은 어떻게 생각하시나 궁금합니다.

값이 없는 tensor (ex: activation) 같은 경우에는 어떤식으로 매칭을 하는건가요?

Activation

nn.Module.named_modules가 OrderedDict로 Model 내의 Module의 등록 된 순서로 iterate 할 수 있어서, activation이 존재하는 경우 Operator 직후에 나온다는 것으로 가정하여 Activation이 속한 Operator에 대한 정보를 얻습니다.

Input Mapping Circle 내에서 Mapping 된 정보를 이용하여(기존에 매핑 되어있는 Tensor Index 집합), 각 Operator에서 사용되는 정보(Operator에 속한 Tensor의 Index 집합) 간의 포함 관계를 이용하여 Operator 내에 아직 매핑 되지 않은 Tensor의 정보를 알아낼 수 있습니다. 매핑 되지 않은 Tensor가 1개인 경우 명확하게 해당 Operator의 output으로 매핑 할 수 있습니다. (2개 이상인 경우 Mapping 하지 못 하고 사용자가 수동으로 매핑해야함을 가정하였습니다.)

Mapping 매핑된 결과를 확인하는 과정에서 Operator의 output으로 mapping 할 수 있는 정보가 존재하는 경우, 해당 Operator의 Quantization Parameter를 json에 출력하여 Activation Quantization이 이루어지게 하였습니다. (PyTorch 1.7.0에서 Quantization을 지원하는 Activation을 확인하였을 때 RELU와 같이 Activation 자체가 Operator와 다른 Quantization Parameter를 가질 수 없다 생각하였습니다.)

감사합니다.

0minyoung0 commented 1 year ago

Exception Retry

중복되는 Tensor 값이 존재하는 경우 Exception을 발생 시키고, 원본 모델의 Tensor 값을 변경한 후 다시 매핑을 시도하는 방식도 가능할 것 같은데 이 방법은 어떻게 생각하시나 궁금합니다.

해당 방식의 충돌 가능성에 대해 첨언합니다. 충돌 가능성은 tensor shape : [1], float 32비트로 가정하였을 때 아래와 같이 계산됩니다.

def calculate_non_duplicate_probability(n):
    p = 1.0
    total = 2 ** 32

    for i in range(n):
        p *= (total - i) / total

    return p

시행 횟수 1000일 때 중복되지 않을 확률: 0.9998837078 시행 횟수 2000일 때 중복되지 않을 확률: 0.9995346798 시행 횟수 3000일 때 중복되지 않을 확률: 0.9989531594 시행 횟수 4000일 때 중복되지 않을 확률: 0.9981395527 시행 횟수 5000일 때 중복되지 않을 확률: 0.9970944273 시행 횟수 6000일 때 중복되지 않을 확률: 0.9958185118 시행 횟수 7000일 때 중복되지 않을 확률: 0.9943126953 시행 횟수 8000일 때 중복되지 않을 확률: 0.9925780260 시행 횟수 9000일 때 중복되지 않을 확률: 0.9906157100 시행 횟수 10000일 때 중복되지 않을 확률: 0.9884271100

해당 방식이 괜찮다면 Retry를 처리하는 if문의 범위를 3이상 정도로 잡는 것을 예상하고 있습니다.