Adding broadcasting to most operations is definitely on our roadmap, and will be hopefully ready quite soon. Since it’s so often asked for we’ll probably reprioritize that and have it implemented soonish.
For now there are two solutions - one that works already, another one that will be implemented very soon (presented in the same order):
You can do broadcasting by manually adding singleton dimensions and expanding along them. This doesn’t do any memory copy, and only does some stride tricks:
net_input = x.mm(weights)
net_input += bias.unsqueeze(0).expand_as(net_input)

Another way, that’s only slightly more convenient is to add a .broadcast function that combines possible many unsqueezes and expands into a single call. You’d just pass in the desired size.

I realize that it might not be a suggestion for a tutorial, where you want to show all the ops, but if you want to do this in your code right now, I’d use linear function from torch.nn.functional:
import torch.nn.functional as F
output = F.linear(input, weights, bias) # bias is optional