To the first question, yes you can. Everytime you call loss.backward() it accumulates gradients in the network. They actually only get processed when you call optimizer.step(), at the end of the loop.
You can only do this since your optimizer.zero_grad() is outside of the loop, once per epoch. If zero_grad was done in the inner loop, once per batch which is (arguably) more common in docs and examples, then gradients would be reseted to zero after every batch.
To the second question: again yes. If you sum losses instead of averaging, your code will yield same results regardless of your batch size.