FINN : 샘플 실행 ONNX(1)
FINN docker로 jupyter를 실행하면 위와같이 작업 디렉토리가 나온다. basics부터 하나씩 분석해 보고자 한다. 0_how_to_work_with_onnx가 순서에 맞는거 같으니 이 파일부터 보려고 한다.
jupyer 내용을 그대로 봐도 좋지만 정리를 위해서 따로 설명을 달고 공유하고자 한다.
import onnx
Add1_node = onnx.helper.make_node(
'Add',
inputs=['in1', 'in2'],
outputs=['sum1'],
name='Add1'
)
위 코드는 node
를 만드는 부분이다. 어떤 노드인지(add), 입력과 출력 이름을 설정 한다.
굳이 설명을 자세히 하자면... Add1_node이라는 변수는 onnx node이고, 이것은 더하기를 하는 것이고, in1, in2를 입력으로 받아서 sum1을 리턴하고, 이 노드의 이름은 Add1 이다,
Add2_node = onnx.helper.make_node(
'Add',
inputs=['sum1', 'in3'],
outputs=['sum2'],
name='Add2',
)
Add3_node = onnx.helper.make_node(
'Add',
inputs=['abs1', 'abs1'],
outputs=['sum3'],
name='Add3',
)
Abs_node = onnx.helper.make_node(
'Abs',
inputs=['sum2'],
outputs=['abs1'],
name='Abs'
)
Round_node = onnx.helper.make_node(
'Round',
inputs=['sum3'],
outputs=['out1'],
name='Round',
)
위와 같이 노드를 막 만들 수 있다. Add 뿐만 아니라 Abs, Round도 위와같이 정의 할 수 있다. 그냥 눈으로 쓱 보고 지나가면 대충 아는거 같은 착각이 들지만 이런 구조를 처음 본다면 한 번 쯤 천천히 꼼꼼히 보기를 권장한다.
Add2_node는 sum1(Add1_node의 결과)과 in3를 더해서 sum2를 결과로 낸다.
Add3_node는 abs1과 abs1을 더해서 sum3를 결과로 낸다. (abs는 아직 안나왔다)
Abs_node는 sum2(Add2_node의 결과)를 Abs하고, 결과 abs1을 결과로 낸다. (이게 Add3_node로 들어간다.)
Round_node는 sum3(Add3_node의 결과)를 Round해서 결과 out1을 낸다.
in1 = onnx.helper.make_tensor_value_info(in1, onnx.TensorProto.FLOAT, [4, 4])
in2 = onnx.helper.make_tensor_value_info(in2, onnx.TensorProto.FLOAT, [4, 4])
in3 = onnx.helper.make_tensor_value_info(in3, onnx.TensorProto.FLOAT, [4, 4])
out1 = onnx.helper.make_tensor_value_info(out1, onnx.TensorProto.FLOAT, [4, 4])
노드에 대한 타입을 설정하는 부분이다. 입출력에 대해서만있고, 입력은 위에서 봤듯이 3개, 출력은 1개가 있다.
graph = onnx.helper.make_graph(
nodes=[
Add1_node,
Add2_node,
Abs_node,
Add3_node,
Round_node,
],
name=simple_graph,
inputs=[in1, in2, in3],
outputs=[out1],
value_info=[
onnx.helper.make_tensor_value_info(sum1, onnx.TensorProto.FLOAT, [4, 4]),
onnx.helper.make_tensor_value_info(sum2, onnx.TensorProto.FLOAT, [4, 4]),
onnx.helper.make_tensor_value_info(abs1, onnx.TensorProto.FLOAT, [4, 4]),
onnx.helper.make_tensor_value_info(sum3, onnx.TensorProto.FLOAT, [4, 4]),
],
)
위에서 정의된 Node를 이용해서 그래프를 만드는 것이다.
onnx_model = onnx.helper.make_model(graph, producer_name=simple-model)
onnx.save(onnx_model, '/tmp/simple_model.onnx')
위에서 만든 그래프를 모델로 만들고, 이것을 파일로 저장하는 부분이다.
from finn.util.visualization import showInNetron
showInNetron('/tmp/simple_model.onnx')
Netron이라는 오픈소스를 이용해서 그래프를 시각화 하는 부분이다.
정상적으로 실행이 되었다면 위와같이 아주 예쁘게 그래프가 출력될 것인데, 나같은 경우는 오류가 발생했다.. docker로 어렵게 설치 했는데 오류가 났지만... 조금만 디버깅 하면 해결이 된다.
showInNetron
에서 오류가 발생할 때..
Docker 컨테이너 내부의 작업 디렉토리를 Host의 {finn}/src에 매핑되는데, 여기에 있는 src/finn/util/visualization.py
파일에 netron.start
쪽의 파라미터가 문제가 있다. 아마 Python 패키지 버전이 업데이트 되면서 파라미터가 바뀌었는데, 적용이 안된 것 같다.
# 수정 전
# netron.start(model_filename, port=8081, host=0.0.0.0)
# 수정 후
netron.start(model_filename, address=('0.0.0.0', 8081))
host와 port 가 따로 되어 있던걸, address로 한번에 받도록 되어있다.
원격으로 접속 한다면, 아래와 같이 추가로 수정해야 한다. 여기서의 주소는 진짜 접속하는 원격 주소이기 때문에 내 경우에는 개발 Host PC 내부 IP 주소로 했다. 외부에서 접속한다면, DDNS나 맞는 주소를 쓰고, 공유기에서 포트포워딩을 하면 될 것이다.
return IFrame(src=http://192.168.100.200:8081/, width=100%, height=400)
이렇게 처리를 하면 아마 jupyter에서 정상적으로 위에 있는대로 아주 예쁘개 그래프가 나올것이다. 드래그하고 클릭하고 하면 자세한 정보를 볼 수 있다.
import numpy as np
def expected_output(in1, in2, in3):
sum1 = np.add(in1, in2)
sum2 = np.add(sum1, in3)
abs1 = np.absolute(sum2)
sum3 = np.add(abs1, abs1)
return np.round(sum3)
이렇게 만들어 놓은 numpy를 이용한 python 함수와 onnx 비교를 한다. 랜덤으로 초기화 된 값을 두 함수에 넣으면 당연히 같은 결과가 나온다.