clone과 detach, 그리고 계산 그래프

Pytorch에서 Tensor를 다룰 때 경고문을 자주 보았을 것이다.

UserWarning: To copy construct from a tensor, it is recommended to use
sourceTensor.detach().clone() ... rather than torch.tensor(sourceTensor).

왜 저 방식을 권장하는지, sourceTensor.detach().clone()이 무슨 일을 하는지, clonedetach를 나눠서 정리해봤다.

clone(): 깊은 복사#

clone()은 원본 Tensor, 즉 sourceTensordeepcopy 객체를 반환한다. 즉 원본과 메모리를 공유하지 않는 독립된 복사본을 만든다. 값을 바꿔도 원본에 영향을 주지 않는다.

다만 clone()만 쓰면 한 가지 함정이 있다. 원본이 requires_grad=True이면, clone으로 만든 텐서도 계산 그래프에 연결된 채로 나온다. 새로운 Tensor가 계산 그래프에서 sourceTensor를 부모로 가지는 자식 노드가 되는 것이다.

detach(): 계산 그래프에서 분리#

detach()는 원본 Tensor를 계산 그래프에서 분리한다.

PyTorch에서는 어떤 Tensor에 대해 requires_grad=True로 설정하면 그 Tensor는 계산 그래프에 추가된다. 여기서 계산 그래프란, PyTorch의 핵심 기능인 autograd를 수행하기 위해 requires_grad=True인 Tensor들을 기록해두는 구조다. 이 그래프를 따라 역전파가 일어난다.

그런데 특정 텐서를 따로 추출해서 쓰고 싶을 때, 앞서 말했듯 clone()만 쓰면 requires_grad=True 상태로 나와 계산 그래프에 자식 노드로 매달린다. 이걸 끊어주는 게 detach다.

detach().clone()#

두 메서드를 합친 sourceTensor.detach().clone()은, sourceTensor를 계산 그래프에서 분리(requires_grad=False)한 후 deepcopy한 객체를 반환한다. 그래서 원본과 메모리도 공유하지 않고 그래프에도 매달리지 않는, 완전히 독립적인 복사본을 얻는다.

copied = source_tensor.detach().clone()

반면에 텐서로부터 새 텐서를 만들 때 torch.tensor(sourceTensor)를 쓰면 다음과 같은 경고가 뜬다.

UserWarning: To copy construct from a tensor, it is recommended to use
sourceTensor.detach().clone() ... rather than torch.tensor(sourceTensor).

한 가지 짚어둘 점은, torch.tensor(sourceTensor)도 사실 데이터를 새 메모리에 복사한다는 것이다. 원본과 메모리를 공유하지는 않는다. 그럼에도 경고가 뜨는 이유는 메모리 공유 때문이 아니라, 이 방식이 비효율적이고 requires_grad 정보 및 계산 그래프 정보를 의도대로 처리하지 못하기 때문이다. 그래서 텐서를 복사할 때는 torch.tensor(...)로 감싸는 대신, 계산 그래프 분리까지 명시적으로 처리하는 detach().clone()을 쓰는 것이 권장된다.

댓글