Why BERT?
BERT (Bidirectional Encoder Representations from Transformers) has revolutionized NLP by capturing contextual relationships within text. Unlike traditional word embeddings that assign a single vector to each word, BERT generates dynamic embeddings that adapt to the surrounding words, leading to a deeper understanding of sentence meaning.
Building the Flask Application
We’ll create a simple Flask application that allows users to input two sentences and receive their similarity score and embeddings.
1. Setting up the Environment:
First, ensure you have Python installed. Then, install the necessary libraries:
pip install flask transformers torch scikit-learn
2. The Python Code (app.py):
Here's the backend Flask application code:
from flask import Flask, request, jsonify, render_template_string
from transformers import BertTokenizer, BertModel
import torch
from sklearn.metrics.pairwise import cosine_similarity
app = Flask(__name__)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
def get_embedding(sentence):
inputs = tokenizer(sentence, return_tensors='pt', truncation=True, padding=True, max_length=128)
outputs = model(**inputs)
return outputs.last_hidden_state.mean(dim=1).detach().numpy()
@app.route('/')
def index():
return render_template_string('''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BERT Sentence Similarity</title>
<style>
/* CSS styles as defined in this HTML file */
</style>
</head>
<body>
<div class="container">
<h1>BERT Sentence Similarity Calculator</h1>
<form id="similarity-form">
<label for="sentence1">Sentence 1:</label>
<textarea id="sentence1" name="sentence1" rows="4" required></textarea>
<label for="sentence2">Sentence 2:</label>
<textarea id="sentence2" name="sentence2" rows="4" required></textarea>
<button type="submit">Calculate Similarity</button>
<button type="button" id="clear-button">Clear</button>
</form>
<div id="results-section" style="display: none;">
<h2>Results</h2>
<p id="similarity-score"></p>
<h3>Sentence 1 Embedding:</h3>
<div class="embedding-table-container">
<table id="embedding1-table">
<thead>
<tr><th>Index</th><th>Value</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<h3>Sentence 2 Embedding:</h3>
<div class="embedding-table-container">
<table id="embedding2-table">
<thead>
<tr><th>Index</th><th>Value</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="collapsible-header" id="info-header">How it Works</div>
<div class="collapsible-content" id="info-content">
<h3>Understanding the Application</h3>
<p>This application uses the BERT (Bidirectional Encoder Representations from Transformers) model to convert sentences into numerical vectors, known as embeddings. These embeddings capture the semantic meaning of the sentences.</p>
<p>The cosine similarity is then calculated between these two sentence embeddings. Cosine similarity measures the cosine of the angle between two vectors. A score close to 1 indicates high similarity, while a score close to 0 indicates low similarity.</p>
<h3>Key Components:</h3>
<ul>
<li><strong>BERT Model & Tokenizer:</strong> Loaded from the Hugging Face Transformers library to process text.</li>
<li><strong>get_embedding(sentence) function:</strong> Converts a given sentence into its BERT embedding.</li>
<li><strong>Flask Routes:</strong> Handles web requests for the main page (/), embedding calculation (/embed), and similarity calculation (/similarity).</li>
<li><strong>Cosine Similarity:</strong> Used from sklearn.metrics.pairwise to quantify the semantic similarity between two embeddings.</li>
</ul>
</div>
</div>
<script>
document.getElementById('similarity-form').addEventListener('submit', async function(event) {
event.preventDefault();
const sentence1 = document.getElementById('sentence1').value;
const sentence2 = document.getElementById('sentence2').value;
const response = await fetch('/similarity', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ sentence1, sentence2 })
});
const data = await response.json();
document.getElementById('similarity-score').textContent = `Similarity Score: ${data.similarity.toFixed(4)}`;
const embedding1TableBody = document.querySelector('#embedding1-table tbody');
embedding1TableBody.innerHTML = '';
data.embedding1[0].forEach((value, index) => {
const row = embedding1TableBody.insertRow();
const indexCell = row.insertCell();
const valueCell = row.insertCell();
indexCell.textContent = index;
valueCell.textContent = value.toFixed(6);
});
const embedding2TableBody = document.querySelector('#embedding2-table tbody');
embedding2TableBody.innerHTML = '';
data.embedding2[0].forEach((value, index) => {
const row = embedding2TableBody.insertRow();
const indexCell = row.insertCell();
const valueCell = row.insertCell();
indexCell.textContent = index;
valueCell.textContent = value.toFixed(6);
});
document.getElementById('results-section').style.display = 'block';
});
document.getElementById('clear-button').addEventListener('click', function() {
document.getElementById('sentence1').value = '';
document.getElementById('sentence2').value = '';
document.getElementById('results-section').style.display = 'none';
document.getElementById('similarity-score').textContent = '';
document.querySelector('#embedding1-table tbody').innerHTML = '';
document.querySelector('#embedding2-table tbody').innerHTML = '';
});
document.getElementById('info-header').addEventListener('click', function() {
const content = document.getElementById('info-content');
if (content.style.display === 'block') {
content.style.display = 'none';
} else {
content.style.display = 'block';
}
});
</script>
</body>
</html>
''')
@app.route('/embed', methods=['POST'])
def embed():
data = request.json
sentence = data.get('sentence', '')
embedding = get_embedding(sentence)
return jsonify({'embedding': embedding.tolist()})
@app.route('/similarity', methods=['POST'])
def similarity():
data = request.json
sentence1 = data.get('sentence1', '')
sentence2 = data.get('sentence2', '')
embedding1 = get_embedding(sentence1)
embedding2 = get_embedding(sentence2)
similarity_score = cosine_similarity(embedding1, embedding2)[0][0]
return jsonify({'similarity': float(similarity_score), 'embedding1': embedding1.tolist(), 'embedding2': embedding2.tolist()})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
3. Understanding the Code:
- BERT Model and Tokenizer: We load the pre-trained BERT model and tokenizer.
get_embedding(sentence): This function tokenizes the input sentence, passes it through the BERT model, and returns the mean of the last hidden state as the sentence embedding.- Flask Routes:
/: Renders the HTML form for inputting sentences./embed: receives a sentence via POST request and returns its embedding./similarity: receives two sentences, calculates their embeddings, computes the cosine similarity, and returns the results.
- Cosine Similarity: We use
sklearn.metrics.pairwise.cosine_similarityto calculate the similarity score between the two sentence embeddings.
4. Running the Application:
Save the code as app.py and run it from your terminal:
python app.py
Open your browser and navigate to http://0.0.0.0:5000. You'll see the form where you can input two sentences and get their similarity score and embeddings.
5. HTML Structure and Functionality
The HTML portion of the code provides a user-friendly interface. It includes:
- Two input fields for the sentences.
- A “Submit” button to trigger the similarity calculation.
- A “Clear” button to reset the form.
- Tables to display the similarity score and embeddings.
- A collapsable section that explains how the application works.
- Javascript to handle the form submission, clear the form, and toggle the collapsable section.
6. Practical Applications:
This application can be extended for various NLP tasks:
- Semantic Search: Improve search results by ranking documents based on semantic similarity to the query.
- Question Answering: Find answers to questions by comparing the question’s embedding with the embeddings of candidate answers.
- Text Clustering: Group similar documents or sentences together.
- Paraphrase Detection: Identify sentences with similar meanings.
- Chatbots: Enhance chatbot responses by understanding the semantic intent of user input.