You could try a recurrent model to identify the part, such that it reads the strings "letter by letter". I would extract the circuit number and parse that by hand, then feed the textual part into the network. In TensorFlow a basic example could look something like this:
import tensorflow as tf
# Words used by people
TAGS = [
'BT',
'SW',
'LBLB-F',
'LBLB',
# ...
]
# "Component id"
LABELS = [
0, # 0 => Battery
1, # 1 => Circuit switch
2, # 2 => Fluroescent light bulb
3, # 3 => Light bulb
# ...
]
# Number of different components
NUM_CLASSES = 4
NUM_LAYERS = 1
LAYER_SIZE = 64
BATCH_SIZE = 100
NUM_EPOCHS = 100
# Inputs must be strings of the same size padded with '\0'
input_tags = tf.placeholder(tf.string, [None], name='Input')
# Convert to ascii values
tag_ascii = tf.decode_raw(input_tags, tf.uint8)
# Get actual lengths
mask = ~tf.equal(tag_ascii, 0)
tag_length = tf.reduce_sum(tf.cast(mask, tf.int32), axis=1)
# Convert to one-hot encoding
tag_1h = tf.one_hot(tag_ascii, 256, dtype=tf.float32)
# RNN
cells = [tf.nn.rnn_cell.BasicLSTMCell(LAYER_SIZE) for _ in range(NUM_LAYERS)]
rnn = tf.nn.rnn_cell.MultiRNNCell(cells)
rnn_output, _ = tf.nn.dynamic_rnn(rnn, tag_1h, sequence_length=tag_length, dtype=tf.float32)
# Get last RNN output
last_rnn_indices = tf.stack([tf.range(tf.shape(rnn_output)[0]), tag_length - 1], axis=-1)
rnn_last_output = tf.gather_nd(rnn_output, last_rnn_indices)
# Output layer
output_weights = tf.get_variable('OutputWeights', (LAYER_SIZE, NUM_CLASSES))
output_logit = rnn_last_output @ output_weights
# Final output as distribution and highest-scoring class
output_dist = tf.nn.softmax(output_logit)
output_class = tf.argmax(output_logit, axis=-1)
# Loss and training
input_labels = tf.placeholder(tf.int32, [None], name='Class')
loss = tf.losses.sparse_softmax_cross_entropy(labels=input_labels, logits=output_logit)
# Choose optimizer and hyperparameters
train_op = tf.train.AdamOptimizer().minimize(loss)
# Variable initialization
init_op = tf.global_variables_initializer()
# Preprocess words so all have the same size
max_tag_len = max(len(tag) for tag in TAGS)
tags_padded = [tag + '\0' * (max_tag_len - len(tag)) for tag in TAGS]
num_examples = len(tags_padded)
with tf.Session() as session:
session.run(init_op)
# Train
for i_epoch in range(NUM_EPOCHS):
for idx_batch in range(0, num_examples, BATCH_SIZE):
tags_batch = tags_padded[idx_batch:idx_batch + BATCH_SIZE]
labels_batch = LABELS[idx_batch:idx_batch + BATCH_SIZE]
session.run(train_op, feed_dict={input_tags: tags_batch, input_labels: labels_batch})
# Check results
predictions, dist = session.run([output_class, rnn_output], feed_dict={input_tags: tags_padded})
for tag, label, prediction in zip(TAGS, LABELS, predictions):
print('Tag {} is class {} and was predicted to be class {}.'.format(tag, label, prediction))
# Test for an unknown tag: 22LBLB-T should be class 3
tag = '22LBLB-T'
prediction = session.run(output_class, feed_dict={input_tags: [tag]})[0]
print('Tag {} was predicted to be class {}.'.format(tag, prediction))
Output:
Tag BT is class 0 and was predicted to be class 0.
Tag SW is class 1 and was predicted to be class 1.
Tag LBLB-F is class 2 and was predicted to be class 2.
Tag LBLB is class 3 and was predicted to be class 3.
Tag 22LBLB-T was predicted to be class 2.
Which basically takes each string, converts it into vectors of numbers, converts these to one-hot encoding and feeds them to a recurrent network (plus an output layer). In this particular case it pads the strings with null characters at the end so all have the same length.
I added a test at the end for an unseen tag 22LBLB-T
that should be classified as light bulb. In this case the model failed and said it's a fluorescent light bulb, although to be fair it didn't have many clues to figure out the right answer, given the data (in fact, that tag is in this case more similar to the fluorescent light bulb, since it has a hyphen - you could consider filtering filtering characters like hyphens and others if you think they are going to "confuse" the model). In any case, the prediction by the model "makes sense" with respect to the provided data (it didn't predict it to be a battery or circuit switch, which would not make any sense).