[jit] __deepcopy__ for RecursiveScriptModule (#32684)

Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/32684

Previously we have `clone` and `clone_instance`, where `clone` will clone both type
and value, and `clone_instance` only clone the value, both of them are shallow copies.
We need to re-evaluate whether we should expose them as a user facing API.
I think we should hide `clone`, but `clone_instance` might be useful as well, especially
when we are copying a model with very large weights, people might just want to do shallow copy.

This PR adds a `deepcopy` that might be useful as a user API, which deep copies the values, including
Tensor, but we didn't deepcopy `Blob`, `Capsule`, `Future` or `PyObject`.
For more discussions please see the following issue.

fixes: https://github.com/pytorch/pytorch/issues/32519

Test Plan: Imported from OSS

Differential Revision: D21220756

fbshipit-source-id: 476bf11fe82c08fac36e7457879a09f545ffdc5e
This commit is contained in:
Jerry Zhang
2020-04-28 18:44:29 -07:00
committed by Facebook GitHub Bot
parent e5a24a6389
commit 6fa76b8a0c
10 changed files with 232 additions and 0 deletions

View File

@ -62,6 +62,117 @@ void testModuleCloneInstance() {
ASSERT_EQ(m3.attr(attr_name).toInt(), 3);
}
void testModuleDeepcopy() {
auto cu = std::make_shared<CompilationUnit>();
auto cls = ClassType::create("foo.bar", cu, true);
auto str_attr = "str_attr";
auto int_attr = "int_attr";
auto tensor_attr = "tensor_attr";
auto tensor_list_attr = "tensor_list_attr";
cls->addAttribute(int_attr, IntType::get());
cls->addAttribute(str_attr, StringType::get());
cls->addAttribute(tensor_attr, TensorType::get());
cls->addAttribute(tensor_list_attr, ListType::ofTensors());
Module m(cu, cls);
c10::List<at::Tensor> list({at::rand(5), at::rand(5)});
m.setattr(int_attr, IValue(2));
m.setattr(str_attr, IValue("str"));
m.setattr(tensor_attr, at::randn(5));
m.setattr(tensor_list_attr, list);
Module m2 = m.deepcopy();
Module m3 = m.clone_instance();
// Make sure copy works
ASSERT_EQ(m2.attr(int_attr).toInt(), 2);
ASSERT_EQ(m3.attr(int_attr).toInt(), 2);
// Test overlaps
ASSERT_TRUE(!IValue(m2._ivalue()).overlaps(IValue(m._ivalue())));
ASSERT_TRUE(IValue(m3._ivalue()).overlaps(IValue(m._ivalue())));
// Both deepcopy and clone_instance will preserve the type
ASSERT_EQ(m.type(), m2.type());
ASSERT_EQ(m.type(), m3.type());
// change int value of copied instances
m2.setattr(int_attr, IValue(3));
m3.setattr(int_attr, IValue(4));
// Verify value of original instance doesn't change
ASSERT_EQ(m.attr(int_attr).toInt(), 2);
ASSERT_EQ(m2.attr(int_attr).toInt(), 3);
ASSERT_EQ(m3.attr(int_attr).toInt(), 4);
// change Tensor value of copied instances
at::Tensor t1 = m.attr(tensor_attr).toTensor();
at::Tensor t2 =
m2.attr(tensor_attr).toTensor(); // deepcopy will copy the Tensor
at::Tensor t3 = m3.attr(tensor_attr)
.toTensor(); // clone_instance will not copy the Tensor
// check copy works
ASSERT_TRUE(t1.equal(t2));
ASSERT_TRUE(t1.equal(t3));
// zero out t1
t1.zero_();
// check that t2 is not affected because it is a deep copy
ASSERT_TRUE(!t1.equal(t2));
// check that t3 is the same as t1 since it is a shallow copy
ASSERT_TRUE(t1.equal(t3));
}
void testModuleDeepcopyString() {
auto cu = std::make_shared<CompilationUnit>();
auto cls = ClassType::create("foo.bar", cu, true);
auto attr1 = "attr1";
cls->addAttribute(attr1, StringType::get());
std::string str = "str";
Module m(cu, cls);
m.setattr(attr1, str);
auto copied = m.deepcopy();
auto original_str = str;
ASSERT_EQ(copied.attr(attr1).toString()->string(), original_str);
// check string mutation is not reflected in the copied module
str += "str";
ASSERT_EQ(copied.attr(attr1).toString()->string(), original_str);
}
void testModuleDeepcopyAliasing() {
// check deepcopy preserves aliasing
auto cu = std::make_shared<CompilationUnit>();
auto cls = ClassType::create("foo.bar", cu, true);
auto attr1 = "attr1";
auto attr2 = "attr2";
auto attr3 = "attr3";
auto attr4 = "attr4";
cls->addAttribute(attr1, ListType::ofTensors());
cls->addAttribute(attr2, ListType::ofTensors());
cls->addAttribute(attr3, TensorType::get());
cls->addAttribute(attr4, TensorType::get());
Module m(cu, cls);
auto t1 = at::rand(5);
auto t2 = at::rand(5);
auto t3 = at::rand(5);
auto t4 = at::rand({5, 2});
c10::List<at::Tensor> list1({t1, t2});
c10::List<at::Tensor> list2({t1, t3});
// first element of attr1 and attr2 are aliased
m.setattr(attr1, list1);
m.setattr(attr2, list2);
m.setattr(attr3, t4);
m.setattr(attr4, t4.view(-1));
auto copied = m.deepcopy();
// test tensor aliasing
auto copied_attr1_t1 = copied.attr(attr1).toList().get(0);
auto copied_attr2_t1 = copied.attr(attr2).toList().get(0);
ASSERT_TRUE(copied_attr1_t1.isAliasOf(copied_attr2_t1));
// test aliasing from view
auto copied_attr3 = copied.attr(attr3);
auto copied_attr4 = copied.attr(attr3);
ASSERT_TRUE(copied_attr3.isAliasOf(copied_attr4));
}
void testModuleConstant() {
auto cu = std::make_shared<CompilationUnit>();
auto cls = ClassType::create("foo.bar", cu, true);