I am learning Pytorch by going through the tutorial as well. I tried to implement the character-level last for pos and the code is as below. Plz correct me if you find something not reasonable.
# -*- coding: utf-8 -*-

import torch
import torch.autograd as autograd
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)


######################################################################
# Example: An LSTM for Part-of-Speech Tagging
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Prepare data:

def prepare_sequence(seq, to_ix):
    idxs = [to_ix[w] for w in seq]
    tensor = torch.LongTensor(idxs)
    return autograd.Variable(tensor)

training_data = [
    ("The dog ate the apple".split(), ["DET", "NN", "V", "DET", "NN"]),
    ("Everybody read that book".split(), ["NN", "V", "DET", "NN"])
]
word_to_ix = {}
char_to_ix = {}
MAX_WORD_LEN = 0
for sent, tags in training_data:
    for word in sent:
        if word not in word_to_ix:
            word_to_ix[word] = len(word_to_ix)
            for ch in word:
                if ch not in char_to_ix:
                    char_to_ix[ch] = len(char_to_ix)
            if len(word) > MAX_WORD_LEN:
                MAX_WORD_LEN = len(word)
char_to_ix[' '] = len(char_to_ix)
print(word_to_ix)
print(char_to_ix)
print MAX_WORD_LEN
tag_to_ix = {"DET": 0, "NN": 1, "V": 2}

# These will usually be more like 32 or 64 dimensional.
# We will keep them small, so we can see how the weights change as we train.
EMBEDDING_DIM = 16
HIDDEN_DIM = 16
CHAR_EMBEDDING_DIM = 3
CHAR_HIDDEN_DIM = 3

######################################################################
# Create the model:


class LSTMTagger(nn.Module):

    def __init__(self, embedding_dim, hidden_dim, vocab_size, char_hidden_dim, char_embedding_dim, alphabet_size, max_word_len, tagset_size):
        super(LSTMTagger, self).__init__()

        # word embedding
        self.hidden_dim = hidden_dim
        self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim)

        # char embedding
        self.char_hidden_dim = char_hidden_dim
        self.char_embeddings = nn.Embedding(alphabet_size, char_embedding_dim)
        self.char_lstm = nn.LSTM(char_embedding_dim, char_hidden_dim)

        self.overall_hidden_dim = hidden_dim + max_word_len * char_hidden_dim

        # The linear layer that maps from hidden state space to tag space
        self.hidden2tag = nn.Linear(self.overall_hidden_dim, tagset_size)
        self.hidden = self.init_hidden()
        self.char_hidden = self.init_hidden(isChar=True)

    def init_hidden(self, isChar=False):
        # Before we've done anything, we dont have any hidden state.
        # Refer to the Pytorch documentation to see exactly
        # why they have this dimensionality.
        # The axes semantics are (num_layers, minibatch_size, hidden_dim)
        if isChar:
            return (autograd.Variable(torch.zeros(1, 1, self.char_hidden_dim)),
                autograd.Variable(torch.zeros(1, 1, self.char_hidden_dim)))
        else:
            return (autograd.Variable(torch.zeros(1, 1, self.hidden_dim)),
                autograd.Variable(torch.zeros(1, 1, self.hidden_dim)))

    def forward(self, sentence, chars):
        embeds = self.word_embeddings(sentence)
        # print 'LEN SENTENCE', len(sentence)
        # print 'HIDDEN', self.hidden
        lstm_out, self.hidden = self.lstm(
            embeds.view(len(sentence), 1, -1), self.hidden)

        embedc = self.char_embeddings(chars)
        char_lstm_out, self.char_hidden = self.char_lstm(embedc.view(len(chars), 1, -1), self.char_hidden)

        merge_out = torch.cat((lstm_out.view(len(sentence), -1), char_lstm_out.view(len(sentence), -1)), 1)

        tag_space = self.hidden2tag(merge_out)
        tag_scores = F.log_softmax(tag_space, dim=1)
        return tag_scores

######################################################################
# Train the model:


model = LSTMTagger(EMBEDDING_DIM, HIDDEN_DIM, len(word_to_ix), CHAR_EMBEDDING_DIM, CHAR_HIDDEN_DIM, len(char_to_ix), MAX_WORD_LEN, len(tag_to_ix))
loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)

# See what the scores are before training
# Note that element i,j of the output is the score for tag j for word i.
inputs = prepare_sequence(training_data[0][0], word_to_ix)
sent_chars = []
for w in training_data[0][0]:
    sps = ' ' * (MAX_WORD_LEN - len(w))
    sent_chars.extend(list(sps + w))
inputc = prepare_sequence(sent_chars, char_to_ix)
tag_scores = model(inputs, inputc)
print(tag_scores)

for epoch in range(300):  # again, normally you would NOT do 300 epochs, it is toy data
    for sentence, tags in training_data:
        # Step 1. Remember that Pytorch accumulates gradients.
        # We need to clear them out before each instance
        model.zero_grad()

        # Also, we need to clear out the hidden state of the LSTM,
        # detaching it from its history on the last instance.
        model.hidden = model.init_hidden()
        model.char_hidden = model.init_hidden(isChar=True)

        # Step 2. Get our inputs ready for the network, that is, turn them into
        # Variables of word indices.
        sentence_in = prepare_sequence(sentence, word_to_ix)
        sent_chars = []
        for w in sentence:
            sps = ' ' * (MAX_WORD_LEN - len(w))
            sent_chars.extend(list(sps + w))
        char_in = prepare_sequence(sent_chars, char_to_ix)
        targets = prepare_sequence(tags, tag_to_ix)

        # Step 3. Run our forward pass.
        tag_scores = model(sentence_in, char_in)

        # Step 4. Compute the loss, gradients, and update the parameters by
        #  calling optimizer.step()
        loss = loss_function(tag_scores, targets)
        loss.backward()
        optimizer.step()

# See what the scores are after training
inputs = prepare_sequence(training_data[0][0], word_to_ix)
sent_chars = []
for w in training_data[0][0]:
    sps = ' ' * (MAX_WORD_LEN - len(w))
    sent_chars.extend(list(sps + w))
inputc = prepare_sequence(sent_chars, char_to_ix)
tag_scores = model(inputs, inputc)
# The sentence is "the dog ate the apple".  i,j corresponds to score for tag j
#  for word i. The predicted tag is the maximum scoring tag.
# Here, we can see the predicted sequence below is 0 1 2 0 1
# since 0 is index of the maximum value of row 1,
# 1 is the index of maximum value of row 2, etc.
# Which is DET NOUN VERB DET NOUN, the correct sequence!
print(tag_scores)

The results looks like this:
Variable containing:
-0.0829 -2.7836 -4.0300
-6.9329 -0.0083 -4.9270
-3.9040 -3.5350 -0.0506
-0.0214 -4.8225 -4.3353
-4.4914 -0.0152 -5.5591
[torch.FloatTensor of size 5x3]

It looks correct:
 The(0:DET) dog(1:NN) ate(2:V) the(0:DET) apple(1:NN)

The key is to concat the two hidden tensors before they are fed into hidden2tag layer.