How can I copy the paramaters of one model to another in LibTorch?

Alio: <p>How can I copy the paramaters of one model to another in LibTorch? I know how to do it in Torch (Python).</p>
<p><code>net2.load_state_dict(net.state_dict())</code></p>
<p>I have tried with the code below in C++ with quite a bit of work. It didn’t copy one to another.</p>
<pre><code>#include &lt;torch/torch.h&gt;
using namespace torch::indexing;

torch::Device device(torch::kCUDA);

void loadstatedict(torch::nn::Module&amp; model, torch::nn::Module&amp; target_model) {
torch::autograd::GradMode::set_enabled(false);  // make parameters copying possible
auto new_params = target_model.named_parameters(); // implement this
auto params = model.named_parameters(true /*recurse*/);
auto buffers = model.named_buffers(true /*recurse*/);
for (auto&amp; val : new_params) {
    auto name = val.key();
    auto* t = params.find(name);
    if (t != nullptr) {
        t-&gt;copy_(val.value());
    } else {
        t = buffers.find(name);
        if (t != nullptr) {
            t-&gt;copy_(val.value());
            }
        }
    }
}

struct Critic_Net : torch::nn::Module {
    torch::Tensor next_state_batch__sampled_action;
    public:
    Critic_Net() {
        lin1 = torch::nn::Linear(3, 3);
        lin2 = torch::nn::Linear(3, 1);
        lin1-&gt;to(device);
        lin2-&gt;to(device);
    }
    torch::Tensor forward(torch::Tensor next_state_batch__sampled_action) {
        auto h = next_state_batch__sampled_action;
        h = torch::relu(lin1-&gt;forward(h));
        h = lin2-&gt;forward(h);
        return h;
    }
    torch::nn::Linear lin1{nullptr}, lin2{nullptr};
};

auto net = Critic_Net();
auto net2 = Critic_Net();
auto the_ones = torch::ones({3, 3}).to(device);

int main() {
    std::cout &lt;&lt; net.forward(the_ones);
    std::cout &lt;&lt; net2.forward(the_ones);
    loadstatedict(net, net2);
    std::cout &lt;&lt; net.forward(the_ones);
    std::cout &lt;&lt; net2.forward(the_ones);

}
</code></pre>

Matej_Kompanek: <p>try this (you still have to register modules):</p>
<pre><code class="lang-auto">struct Critic_Net : torch::nn::Module {
	torch::Tensor next_state_batch__sampled_action;
	torch::nn::Linear lin1{ nullptr }, lin2{ nullptr };
public:
	Critic_Net() {
		lin1 = register_module("lin1", torch::nn::Linear(3, 3));
		lin2 = register_module("lin2", torch::nn::Linear(3, 1));
	}
	torch::Tensor forward(torch::Tensor next_state_batch__sampled_action) {
		auto h = next_state_batch__sampled_action;
		h = torch::relu(lin1-&gt;forward(h));
		h = lin2-&gt;forward(h);
		return h;
	}

	std::string SaveMemory()
	{
		std::ostringstream oss;
		torch::serialize::OutputArchive archive;

		this-&gt;save(archive);
		archive.save_to(oss);

		return oss.str();
	}

	void LoadMemory(const std::string&amp; mem)
	{
		torch::serialize::InputArchive archive;
		archive.load_from(std::istringstream(mem));
		this-&gt;load(archive);
	}
};


void main()
{
	try
	{
		auto net = Critic_Net();
		auto net2 = Critic_Net();

		auto the_ones = torch::ones({ 3, 3 });

		std::cout &lt;&lt; net.forward(the_ones) &lt;&lt; std::endl &lt;&lt; std::endl;
		std::cout &lt;&lt; net2.forward(the_ones) &lt;&lt; std::endl &lt;&lt; std::endl;

		auto state = net.SaveMemory();
		net2.LoadMemory(state);

		std::cout &lt;&lt; net.forward(the_ones) &lt;&lt; std::endl &lt;&lt; std::endl;
		std::cout &lt;&lt; net2.forward(the_ones) &lt;&lt; std::endl &lt;&lt; std::endl;
	}
	catch (std::runtime_error&amp; e)
	{
		std::cout &lt;&lt; e.what() &lt;&lt; std::endl;
	}
	catch (const c10::Error&amp; e)
	{
		std::cout &lt;&lt; e.msg() &lt;&lt; std::endl;
	}

	system("PAUSE");
}
</code></pre>

Alio: <p>Thank you. But I have one error.</p>
<p>I get an error on the <code>archive.load_from(std::istringstream(mem));</code> line</p>
<pre><code class="lang-auto">/home/iii/tor/m_gym/tensor.cpp: In member function ‘void Critic_Net::LoadMemory(const string&amp;)’:
/home/iii/tor/m_gym/tensor.cpp:88:40: error: cannot bind non-const lvalue reference of type ‘std::istream&amp;’ {aka ‘std::basic_istream&lt;char&gt;&amp;’} to an rvalue of type ‘std::basic_istream&lt;char&gt;’
   88 |                 archive.load_from(std::istringstream(mem));
</code></pre>

Matej_Kompanek: <p>in VS it works, try changing LoadMemory to simple version like this → </p>
<pre><code class="lang-auto">void LoadMemory(std::string mem)
</code></pre>

Alio: <aside class="quote no-group" data-username="Matej_Kompanek" data-post="2" data-topic="164165">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/matej_kompanek/48/12654_2.png" class="avatar"> Matej_Kompanek:</div>
<blockquote>
<p><code>istringstream</code></p>
</blockquote>
</aside>
<p>I’m in VS Code too. I get the error I mentioned or when I change how the <code>mem</code> “string” is handled.</p>
<pre><code class="lang-auto">Unrecognized data format
sh: 1: PAUSE: not found
</code></pre>

Matej_Kompanek: <p>remove</p>
<pre><code class="lang-auto">system("PAUSE");
</code></pre>
<p>VS != VS Code, if you’re on linux, the compiler is different and it doesn’t know PAUSE command</p>

Alio: <p>Ah, okay.<br>
Though, the error is still the same. VS Code doesn’t like this line</p>
<p><div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/7/3/730d8c7472ebec9f771a8b935ace53f8f1379ff9.png" data-download-href="https://discuss.pytorch.org/uploads/default/730d8c7472ebec9f771a8b935ace53f8f1379ff9" title="Screenshot from 2022-10-22 18-13-49"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/7/3/730d8c7472ebec9f771a8b935ace53f8f1379ff9_2_690x421.png" alt="Screenshot from 2022-10-22 18-13-49" data-base62-sha1="gpNW5EsYthQtHXDwbMHQNFgbRGN" width="690" height="421" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/7/3/730d8c7472ebec9f771a8b935ace53f8f1379ff9_2_690x421.png, https://discuss.pytorch.org/uploads/default/original/3X/7/3/730d8c7472ebec9f771a8b935ace53f8f1379ff9.png 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/7/3/730d8c7472ebec9f771a8b935ace53f8f1379ff9.png 2x" data-dominant-color="212222"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">Screenshot from 2022-10-22 18-13-49</span><span class="informations">712×435 32.5 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>If I remove the <code>std::istringstream</code> I get the other <code>Unrecognized data format</code> error.</p>
<p>Sorry, I know some C++, but I’m out of my depth with what you’ve introduced.</p>
<p>When I hover over the red underline it shows…</p>
<p>load_from</p>
<pre><code class="lang-auto">+4 overloads

Loads the `InputArchive` from a serialized representation stored in the
file at `filename`. Storage are remapped using device option. If device
is not specified, the module is loaded to the original device.

no instance of overloaded function "torch::serialize::InputArchive::load_from" matches the argument listC/C++(304)
</code></pre>

Matej_Kompanek: <p><img src="https://discuss.pytorch.org/images/emoji/apple/confused.png?v=12" title=":confused:" class="emoji" alt=":confused:" loading="lazy" width="20" height="20">no idea, missing header maybe ?</p>
<pre><code class="lang-auto">#include &lt;sstream&gt;
</code></pre>

Alio: <p>Nope. I ended up using this. I found it online somewhere and don’t remember the source.</p>
<pre><code class="lang-auto">// Model in 2nd position becomes 1st
void loadstatedict(torch::nn::Module&amp; model, torch::nn::Module&amp; target_model) {
torch::autograd::GradMode::set_enabled(false);  // make parameters copying possible
auto new_params = target_model.named_parameters(); // implement this
auto params = model.named_parameters(true /*recurse*/);
auto buffers = model.named_buffers(true /*recurse*/);
for (auto&amp; val : new_params) {
    auto name = val.key();
    auto* t = params.find(name);
    if (t != nullptr) {
        t-&gt;copy_(val.value());
    } else {
        t = buffers.find(name);
        if (t != nullptr) {
            t-&gt;copy_(val.value());
            }
        }
    }
    torch::autograd::GradMode::set_enabled(true);
}
</code></pre>

Alio: <p>I seem to be able to save the model with this code at the bottom of the <code>main</code> function.</p>
<pre><code class="lang-auto">    std::string model_path = "model.pt";
    torch::serialize::OutputArchive output_archive;
    net.save(output_archive);
    output_archive.save_to(model_path);
</code></pre>
<p>At least this saves a file called “model.pt” in the directory.<br>
Is there a way to load the model somehow with <code>InputArchive</code> at the top of the main function?</p>

Matej_Kompanek: <p>If saving to file is OK, I think you can just use <code>torch::save</code> and <code>torch::load</code>, examples here <a href="https://github.com/pytorch/pytorch/blob/30fb2c4abaaaa966999eab11674f25b18460e609/test/cpp/api/serialize.cpp" rel="noopener nofollow ugc">Github Pytorch cpp api serialize test</a></p>

Alio: <p><code>torch::load</code> and <code>torch::save</code> have always created huge, unspecific errors.</p>

Matej_Kompanek: <p>Doesn’t compile or runtime error ?<br>
I meant something like this (outputs same values after loading)</p>
<pre><code class="lang-auto">struct Critic_Net : torch::nn::Module {
	torch::Tensor next_state_batch__sampled_action;
public:
	Critic_Net() {
		// Construct and register two Linear submodules.
		lin1 = register_module("lin1", torch::nn::Linear(427, 42));
		lin2 = register_module("lin2", torch::nn::Linear(42, 286));
		lin3 = register_module("lin3", torch::nn::Linear(286, 42));
	}
	torch::Tensor forward(torch::Tensor next_state_batch__sampled_action) {
		auto h = next_state_batch__sampled_action;
		h = torch::relu(lin1-&gt;forward(h));
		h = torch::tanh(lin2-&gt;forward(h));
		h = lin3-&gt;forward(h);
		return h;
	}
	torch::nn::Linear lin1{ nullptr }, lin2{ nullptr }, lin3{ nullptr };
};

int main()
{
	try
	{
		auto test_input = torch::zeros({ 1, 427 });

		auto net = std::make_shared&lt;Critic_Net&gt;();
		auto net2 = std::make_shared&lt;Critic_Net&gt;();

		std::cout &lt;&lt; net-&gt;forward(test_input);

		torch::save(net, "temp.pt");
		torch::load(net2, "temp.pt");

		std::cout &lt;&lt; net2-&gt;forward(test_input);
	}
	catch (std::runtime_error&amp; e)
	{
		std::cout &lt;&lt; e.what() &lt;&lt; std::endl;
	}
	catch (const c10::Error&amp; e)
	{
		std::cout &lt;&lt; e.msg() &lt;&lt; std::endl;
	}
}
</code></pre>

Alio: <p>Thank you. The <code>make_shared</code> was holding me back.</p>
<pre><code class="lang-auto">int main() {
    auto net = std::make_shared&lt;Critic_Net&gt;();
    torch::load(net, "temp.pt");
    auto net_optimizer = torch::optim::Adam(net-&gt;parameters(),  1e-3);

    for (int e = 0; e&lt;1000; e++) {
        net_optimizer.zero_grad();
        y_hat = net-&gt;forward(one);

        loss = torch::smooth_l1_loss(target, y_hat);
        loss.backward();
        net_optimizer.step();

        if (e % 50 == 0) {
            std::cout &lt;&lt; loss.item() &lt;&lt; "  " &lt;&lt; "\n";
        }
    }
    torch::save(net, "temp.pt");
}
</code></pre>
<p>I thought it wasn’t working because I expected it to load with a lower loss function. I dimly didn’t think about how the script starts with a new random tensor.</p>

Matej_Kompanek: <p>You’re loading from <code>model.pt</code> and saving to <code>temp.pt</code>, is that right ?<br>
Also, Adam optimizer has some internal state which is not saved, so it may behave different after restart.</p>

Alio: <p>I just fixed that too. I was trying different model names because I was saving with different methods.<br>
It has been a messy journey but looks like a ‘no duh’ situation in hind sight.<br>
I saved the tensors too and load them and now I can save and load a model which improves every time I run the script, which was the goal. Thank you. It would have taken me another day to figure out. When I saw your single tensor example gave the same outputs from the models I knew where I f’d up.</p>