You Shall Know a Word by the Company It Keeps
The distributional hypothesis, and counting context
Week 5 ended on a failure: two sentences meaning the same thing scored lower for similarity than two meaning the opposite. The cause is that every method so far treats words as arbitrary distinct symbols. Film and movie are simply two different columns, no more related than film and hydraulic.
# A small corpus written so that related words share company. Nothing
# in it states that a dog is like a cat; the sentences merely use them
# in the same way, which turns out to be enough.
SENTENCES = [
'the dog barked at the postman in the street',
'the cat slept on the warm windowsill all day',
'the dog slept on the rug by the fire',
'the cat chased a mouse across the kitchen floor',
'my dog eats meat and biscuits every morning',
'my cat eats fish and biscuits every morning',
'the puppy barked and chased the ball in the garden',
'the kitten slept and chased a ball of wool',
'i drove the car down the motorway to london',
'i drove the van down the motorway to manchester',
'the car needs fuel and a service before winter',
'the van needs fuel and new tyres before winter',
'the lorry carried freight along the motorway at night',
'the train carried passengers to london every hour',
'she ate bread and cheese for lunch at her desk',
'he ate bread and jam for breakfast before work',
'the bread was fresh from the bakery this morning',
'the cheese was strong and came from the market',
'london is a large city with an old river',
'manchester is a large city with a busy centre',
]
TOKENS = [s.split() for s in SENTENCES]
VOCAB = sorted({w for s in TOKENS for w in s})
INDEX = {w: i for i, w in enumerate(VOCAB)}
print('%d sentences, %d distinct words' % (len(SENTENCES), len(VOCAB)))
for s in SENTENCES[:4]:
print(' ' + s)
the dog barked at the postman in the street
the cat slept on the warm windowsill all day
the dog slept on the rug by the fire
the cat chased a mouse across the kitchen floor
Counting company
# A small corpus written so that related words share company. Nothing
# in it states that a dog is like a cat; the sentences merely use them
# in the same way, which turns out to be enough.
SENTENCES = [
'the dog barked at the postman in the street',
'the cat slept on the warm windowsill all day',
'the dog slept on the rug by the fire',
'the cat chased a mouse across the kitchen floor',
'my dog eats meat and biscuits every morning',
'my cat eats fish and biscuits every morning',
'the puppy barked and chased the ball in the garden',
'the kitten slept and chased a ball of wool',
'i drove the car down the motorway to london',
'i drove the van down the motorway to manchester',
'the car needs fuel and a service before winter',
'the van needs fuel and new tyres before winter',
'the lorry carried freight along the motorway at night',
'the train carried passengers to london every hour',
'she ate bread and cheese for lunch at her desk',
'he ate bread and jam for breakfast before work',
'the bread was fresh from the bakery this morning',
'the cheese was strong and came from the market',
'london is a large city with an old river',
'manchester is a large city with a busy centre',
]
TOKENS = [s.split() for s in SENTENCES]
VOCAB = sorted({w for s in TOKENS for w in s})
INDEX = {w: i for i, w in enumerate(VOCAB)}
def cooccurrence(window=2):
"""Count how often each pair of words appears near each other."""
M = np.zeros((len(VOCAB), len(VOCAB)))
for sentence in TOKENS:
for i, word in enumerate(sentence):
lo = max(0, i - window)
hi = min(len(sentence), i + window + 1)
for j in range(lo, hi):
if j != i:
M[INDEX[word], INDEX[sentence[j]]] += 1
return M
M = cooccurrence()
def similar(vectors, word, n=5):
v = vectors[INDEX[word]]
norms = np.linalg.norm(vectors, axis=1) * np.linalg.norm(v) + 1e-9
sims = (vectors @ v) / norms
order = np.argsort(sims)[::-1]
return [(VOCAB[i], float(sims[i])) for i in order if VOCAB[i] != word][:n]
for word in ['dog', 'car']:
row = M[INDEX[word]]
top = np.argsort(row)[::-1][:6]
print('%-6s appears near: %s'
% (word, ', '.join('%s(%d)' % (VOCAB[i], row[i])
for i in top if row[i] > 0)))
car appears near: the(3), needs(1), down(1), drove(1), fuel(1)
That row of counts is already a representation of the word, and it is one in which meaning is present. Nothing in the corpus states that a dog is an animal or a car is a vehicle. The information is in the company each word keeps.