Python 3 ve NLTK Kullanarak Duygu Analizi (Sentiment Analysis) Nasıl Yapılır?
Doğal Dil İşleme (NLP – Natural Language Processing), günümüz teknoloji dünyasında insan dilini anlamlandırmak, analiz etmek ve makine öğrenimi modelleriyle işlemek için kullanılan en kritik alanlardan biridir. NLP’nin en popüler ve ticari açıdan en değerli alt dallarından biri ise Duygu Analizi (Sentiment Analysis) veya diğer adıyla fikir madenciliğidir. Duygu analizi; bir metnin yazarının belirli bir konu, ürün veya durum hakkında olumlu, olumsuz veya nötr bir tavır sergileyip sergilemediğini otomatik olarak belirleme sürecidir.
Bu makalede, Python 3 ve Doğal Dil İşleme dünyasının en köklü kütüphanelerinden biri olan Natural Language Toolkit (NLTK) kullanarak adım adım uçtan uca bir duygu analizi modelinin nasıl kurulacağını öğreneceksiniz. Gerçekçi bir senaryo üzerinden ilerlemek adına, Twitter (yeni adıyla X) verileriyle çalışacak, ham metinleri temizleyecek, normalleştirecek ve bir Naive Bayes sınıflandırıcısı eğiterek yeni metinlerin duygu durumunu tahmin edeceğiz.
Duygu Analizinin Önemi ve Kullanım Alanları
Duygu analizi, şirketlerin ve araştırmacıların büyük miktardaki metin verilerinden anlamlı içgörüler elde etmelerini sağlar. Manuel olarak okunması imkansız olan milyonlarca satırlık veriler, bu algoritmalar sayesinde saniyeler içinde analiz edilebilir. Başlıca kullanım alanları şunlardır:
- Müşteri Geri Bildirimleri ve Ürün Değerlendirmeleri: E-ticaret sitelerindeki kullanıcı yorumlarının analiz edilerek ürünlerin güçlü ve zayıf yönlerinin belirlenmesi.
- Sosyal Medya İzleme (Social Listening): Bir marka, kampanya veya siyasi figür hakkında sosyal medyada yapılan paylaşımların genel eğiliminin (olumlu/olumsuz) ölçülmesi.
- Finansal Analiz: Haber başlıklarının ve finansal raporların analiz edilerek borsa eğilimlerinin veya hisse senedi hareketlerinin tahmin edilmesi.
- Müşteri Hizmetleri: Destek taleplerinin (ticket) aciliyetini ve müşterinin öfke düzeyini tespit ederek önceliklendirme yapılması.
Gereksinimler ve Çalışma Ortamının Hazırlanması
Bu kılavuzu takip edebilmek için bilgisayarınızda Python 3’ün kurulu olması gerekmektedir. Projemizde kullanacağımız temel kütüphane NLTK’dir. Ayrıca metin temizleme işlemleri için Python’un yerleşik düzenli ifadeler (regular expressions – re) kütüphanesinden yararlanacağız.
İlk olarak terminalinizi veya komut satırınızı açarak NLTK kütüphanesini sisteminize yükleyin:
pip install nltk
NLTK kütüphanesi kurulduktan sonra, kütüphanenin sunduğu veri kümelerini (corpora) ve dil modellerini indirmemiz gerekir. Python etkileşimli kabuğunu (Python REPL) veya bir Python dosyasını açarak aşağıdaki kodları çalıştırın:
import nltk
Twitter veri kümesini indiriyoruz
nltk.download('twitter_samples')
Metinleri kelimelere ayırmak için gerekli olan tokenizer modelini indiriyoruz
nltk.download('punkt')
Kelimelerin köklerini bulmak (lemmatization) için WordNet veritabanını indiriyoruz
nltk.download('wordnet')
nltk.download('omw-1.4')
Kelime türlerini (isim, fiil vb.) belirlemek için POS tagger modelini indiriyoruz
nltk.download('averaged_perceptron_tagger')
Etkisiz kelimeleri (stopwords) indiriyoruz
nltk.download('stopwords')
Bu indirmeler tamamlandığında, projemiz için gerekli olan tüm veri tabanları ve modeller yerel diskinize kaydedilmiş olacaktır. Artık veri setimizi incelemeye başlayabiliriz.
Adım 1: Veri Setini İncelemek ve Yüklemek
NLTK, duygu analizi modellerini eğitmek ve test etmek için hazır bir Twitter veri kümesi (twitter_samples) sunar. Bu veri kümesinde 5.000 adet olumlu (positive) tweet, 5.000 adet olumsuz (negative) tweet ve 20.000 adet genel (nötr/karışık) tweet bulunmaktadır. Biz bu çalışmada dengeli bir sınıflandırma yapmak için olumlu ve olumsuz tweetleri kullanacağız.
Veri setini yüklemek ve içeriğine göz atmak için aşağıdaki kodu yazalım:
from nltk.corpus import twitter_samples
Olumlu ve olumsuz tweetleri yüklüyoruz
positive_tweets = twitter_samples.strings('positive_tweets.json')
negative_tweets = twitter_samples.strings('negative_tweets.json')
print(f"Toplam Olumlu Tweet Sayısı: {len(positive_tweets)}")
print(f"Toplam Olumsuz Tweet Sayısı: {len(negative_tweets)}")
Örnek tweetleri yazdıralım
print("\nÖrnek Olumlu Tweet:")
print(positive_tweets[0])
print("\nÖrnek Olumsuz Tweet:")
print(negative_tweets[0])
Yukarıdaki kodu çalıştırdığınızda, tweetlerin ham hallerini göreceksiniz. Tweetler genellikle emojiler, kullanıcı etiketleri (@kullanici), web bağlantıları (URL) ve noktalama işaretleri gibi makine öğrenimi modellerinin anlamlandırmakta zorlanacağı “gürültülü” (noisy) öğeler içerir. Bu nedenle, modelimizi eğitmeden önce metin ön işleme (text preprocessing) adımlarını uygulamamız şarttır.
Adım 2: Metin Ön İşleme ve Normalizasyon
Doğal Dil İşleme projelerinin başarısı, büyük oranda verinin ne kadar iyi temizlendiğine bağlıdır. Metin ön işleme süreci şu aşamalardan oluşur:
- Tokenization (Belirteçlere Ayırma): Metni kelimeler, sayılar ve noktalama işaretleri gibi anlamlı en küçük birimlere (token) bölmek.
- Gürültü Temizleme (Noise Removal): Köprü metinleri (URL’ler), Twitter kullanıcı adları (@ işaretleri), özel karakterler ve noktalama işaretlerinin kaldırılması.
- Stopwords (Etkisiz Kelimeler) Filtreleme: “and”, “the”, “is”, “a” gibi cümleye dilbilgisel yapı kazandıran ancak duygu yönünden anlam ifade etmeyen sık kullanılan kelimelerin çıkarılması.
- Lemmatization (Sözlük Birimleştirme): Kelimeleri köklerine indirgemek. Örneğin, “running”, “runs” ve “ran” kelimelerini ortak kök olan “run” kelimesine dönüştürmek. Bu işlem, modelin farklı çekim ekleri almış aynı kelimeleri tek bir kavram olarak öğrenmesini sağlar.
Kelime Türlerinin Belirlenmesi (POS Tagging) ve Lemmatizasyon
NLTK’deki WordNetLemmatizer sınıfı, bir kelimeyi doğru şekilde köküne indirgeyebilmek için o kelimenin cümle içindeki türüne (isim, fiil, sıfat vb.) ihtiyaç duyar. Bu işleme POS (Part-of-Speech) Tagging denir. Kelimenin türünü WordNet formatına dönüştüren yardımcı bir fonksiyon yazalım:
from nltk.tag import pos_tag
from nltk.stem import WordNetLemmatizer
def get_wordnet_pos(tag):
"""POS etiketini WordNet formatına dönüştürür."""
if tag.startswith('J'):
return 'a' # Adjective (Sıfat)
elif tag.startswith('V'):
return 'v' # Verb (Fiil)
elif tag.startswith('N'):
return 'n' # Noun (İsim)
elif tag.startswith('R'):
return 'r' # Adverb (Zarf)
else:
return 'n' # Varsayılan olarak isim kabul ediyoruz
Gürültü Temizleme ve Temizleme Fonksiyonunun Yazılması
Şimdi, yukarıdaki fonksiyonu ve düzenli ifadeleri (regex) kullanarak tüm temizleme adımlarını gerçekleştirecek olan clean_text fonksiyonunu oluşturalım:
import re
import string
from nltk.corpus import stopwords
def clean_text(tweet_tokens):
cleaned_tokens = []
lemmatizer = WordNetLemmatizer()
stop_words = set(stopwords.words('english'))
# Kelime türlerini analiz ediyoruz
pos_tags = pos_tag(tweet_tokens)
for token, tag in pos_tags:
# URL'leri temizliyoruz (http:// veya https:// ile başlayanlar)
token = re.sub(r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", '', token)
# Twitter kullanıcı adlarını temizliyoruz (@kullanici)
token = re.sub(r"(@[A-Za-z0-9_]+)", '', token)
# Kelime türüne göre kök bulma (Lemmatization) işlemini uyguluyoruz
pos = get_wordnet_pos(tag)
token = lemmatizer.lemmatize(token, pos)
# Karakter uzunluğu kontrolü, noktalama işaretleri ve stopword filtrelemesi
if len(token) > 0 and token not in string.punctuation and token.lower() not in stop_words:
cleaned_tokens.append(token.lower())
return cleaned_tokens
Bu fonksiyon, girdi olarak token’lara ayrılmış bir liste alır; gürültüleri temizler, kelimeleri köklerine indirger, etkisiz kelimeleri atar ve tamamen temizlenmiş küçük harfli token listesi döndürür.
NLTK’nin twitter_samples.tokenized() metodu, tweetleri doğrudan token listesi olarak almanızı sağlar. Fonksiyonumuzu test etmek için bir örnek yapalım:
# Tokenize edilmiş ham tweet listesini alalım
pos_tweet_tokens = twitter_samples.tokenized('positive_tweets.json')
İlk tweetin ham halini ve temizlenmiş halini karşılaştıralım
print("Ham Tokenlar:")
print(pos_tweet_tokens[0])
print("\nTemizlenmiş ve Normalize Edilmiş Tokenlar:")
print(clean_text(pos_tweet_tokens[0]))
Çıktıyı incelediğinizde, emojilerin (örneğin gülen yüz “:)” gibi duygu belirten ifadelerin) korunduğunu, ancak gereksiz kelimelerin ve noktalama işaretlerinin elendiğini göreceksiniz. Emojiler duygu analizinde çok güçlü sinyaller taşıdığı için temizleme esnasında tamamen silinmemeleri önemlidir.
Adım 3: Verilerin Model İçin Hazırlanması (Öznitelik Çıkarımı)
Makine öğrenimi modelleri doğrudan metinler veya kelime listeleriyle çalışamaz. Kelimeleri, modellerin anlayabileceği sayısal veya yapısal formata dönüştürmemiz gerekir. NLTK’nin Naive Bayes Sınıflandırıcısı (Naive Bayes Classifier), girdi olarak bir sözlük (dictionary) yapısı bekler. Bu sözlükte anahtarlar (keys) kelimeleri, değerler (values) ise o kelimenin metinde var olup olmadığını belirten Boolean (True) değerini temsil eder.
Bu dönüşümü gerçekleştirecek olan bir jeneratör fonksiyon yazalım:
def get_tweets_for_model(cleaned_tokens_list):
"""Token listesini NLTK Naive Bayes formatına (sözlük yapısına) dönüştürür."""
for tweet_tokens in cleaned_tokens_list:
yield dict([token, True] for token in tweet_tokens)
Şimdi tüm olumlu ve olumsuz tweetleri temizleyelim ve model formatına dönüştürelim:
# 1. Tüm tweetleri tokenize edilmiş olarak yüklüyoruz
positive_tweet_tokens = twitter_samples.tokenized('positive_tweets.json')
negative_tweet_tokens = twitter_samples.tokenized('negative_tweets.json')
2. Tüm tweetleri temizliyoruz
cleaned_positive_tokens = [clean_text(tokens) for tokens in positive_tweet_tokens]
cleaned_negative_tokens = [clean_text(tokens) for tokens in negative_tweet_tokens]
3. Model formatına (Dictionary) dönüştürüyoruz
positive_dataset = list(get_tweets_for_model(cleaned_positive_tokens))
negative_dataset = list(get_tweets_for_model(cleaned_negative_tokens))
Bu aşamada elimizde iki adet liste bulunmaktadır. positive_dataset içerisindeki her bir eleman, olumlu bir tweette geçen temizlenmiş kelimelerin {'harika': True, 'hizli': True} şeklinde haritalanmış halidir.
Adım 4: Eğitim ve Test Veri Setlerinin Oluşturulması
Modelimizin doğruluğunu (accuracy) tarafsız bir şekilde ölçebilmek için elimizdeki verileri ikiye bölmeliyiz: Eğitim Seti (Training Set) ve Test Seti (Test Set). Eğitim setini modeli eğitmek için kullanırken, test setini modelin daha önce hiç görmediği veriler üzerindeki performansını ölçmek için kullanacağız.
Verilerimizi karıştırarak (shuffle) %70’ini eğitim, %30’unu ise test için ayıralım:
import random
Verileri etiketliyoruz (Positive veya Negative)
positive_dataset_labeled = [(tweet_dict, "Positive") for tweet_dict in positive_dataset]
negative_dataset_labeled = [(tweet_dict, "Negative") for tweet_dict in negative_dataset]
İki veri setini birleştiriyoruz
dataset = positive_dataset_labeled + negative_dataset_labeled
Verilerin sırasını rastgele karıştırıyoruz
random.seed(42) # Sonuçların tekrarlanabilir olması için sabit bir seed değeri veriyoruz
random.shuffle(dataset)
Veriyi bölüyoruz (7000 eğitim, 3000 test)
train_data = dataset[:7000]
test_data = dataset[7000:]
print(f"Eğitim Veri Sayısı: {len(train_data)}")
print(f"Test Veri Sayısı: {len(test_data)}")
Adım 5: Naive Bayes Sınıflandırıcısının Eğitilmesi ve Değerlendirilmesi
Naive Bayes, özellikle metin sınıflandırma ve duygu analizi görevlerinde son derece hızlı ve etkili çalışan olasılıksal bir makine öğrenimi algoritmasıdır. Bayes Teoremi’ne dayanır ve metindeki kelimelerin birbirlerinden bağımsız olduğunu varsayar (bu yüzden “Naive” yani saf/safdil olarak adlandırılır). Bu varsayım gerçek dilde her zaman doğru olmasa da, pratik uygulamalarda şaşırtıcı derecede yüksek başarı oranları sunar.
NLTK kütüphanesinin sunduğu hazır Naive Bayes sınıflandırıcısını kullanarak modelimizi eğitelim ve test verisi üzerindeki doğruluğunu ölçelim:
from nltk import classify
from nltk import NaiveBayesClassifier
Modeli eğitiyoruz
classifier = NaiveBayesClassifier.train(train_data)
Modelin doğruluğunu (Accuracy) hesaplıyoruz
accuracy = classify.accuracy(classifier, test_data)
print(f"Modelin Doğruluk Oranı (Accuracy): {accuracy * 100:.2f}%")
Bu kod bloğunu çalıştırdığınızda, modelin yaklaşık olarak %99 civarında bir doğruluk oranına ulaştığını göreceksiniz. Bu, Twitter gibi gürültülü bir platformdan alınan veriler için oldukça yüksek bir başarıdır.
En Bilgi Verici Özniteliklerin (Most Informative Features) İncelenmesi
Naive Bayes sınıflandırıcısının en güzel yanlarından biri, hangi kelimelerin karara ne kadar etki ettiğini şeffaf bir şekilde gösterebilmesidir. Modelin kararlarını en çok etkileyen kelimeleri listelemek için aşağıdaki fonksiyonu kullanabiliriz:
print(classifier.show_most_informative_features(15))
Bu çıktıda, örneğin “:)” emojisinin görüldüğü tweetlerin ezici bir çoğunlukla “Positive” olarak sınıflandırıldığını veya “sad” (üzgün) kelimesinin “Negative” sınıfı için ne kadar belirleyici olduğunu oranlarıyla birlikte görebilirsiniz. Oranlar (Ratio), bir kelimenin bir sınıfta diğer sınıfa kıyasla kaç kat daha sık geçtiğini gösterir (örneğin sad : Positive = 35.2 : 1.0 gibi).
Adım 6: Modelin Yeni ve Özgün Metinler Üzerinde Test Edilmesi
Eğittiğimiz modelin gerçek dünyada nasıl çalıştığını görmek için kendi yazacağımız örnek cümleleri modele girdi olarak verelim. Bu işlem için girdiyi önce tokenize etmeli, ardından temizlemeli ve son olarak sınıflandırıcıya göndermeliyiz. Bu adımları otomatikleştiren bir fonksiyon yazalım:
from nltk.tokenize import word_tokenize
def predict_sentiment(custom_tweet):
# Cümleyi kelimelerine (token) ayırıyoruz
custom_tokens = word_tokenize(custom_tweet)
# Temizleme adımlarını uyguluyoruz
cleaned_tokens = clean_text(custom_tokens)
# Modelin beklediği sözlük formatına dönüştürüyoruz
custom_features = dict([token, True] for token in cleaned_tokens)
# Sınıflandırma yapıyoruz
decision = classifier.classify(custom_features)
# Olasılık dağılımlarını görmek istersek:
prob_dist = classifier.prob_classify(custom_features)
pred_prob = prob_dist.prob(decision)
return decision, pred_prob
Test Cümleleri
test_sentences = [
"I love this product! It works perfectly and saved me a lot of time.",
"This is the worst customer service I have ever experienced. Extremely disappointed.",
"The movie was okay, not great but not terrible either.",
"Thank you for your quick response, I appreciate your help. :)",
"I lost my keys today, what a bad start to the week."
]
print("--- ÖZEL METİN TAHMİNLERİ ---")
for sentence in test_sentences:
sentiment, confidence = predict_sentiment(sentence)
print(f"Metin: '{sentence}'")
print(f"Tahmin: {sentiment} (Güven Oranı: {confidence * 100:.2f}%)\n")
Yukarıdaki kod çalıştırıldığında, modelin olumlu cümleleri yüksek güven oranlarıyla “Positive”, olumsuz cümleleri ise “Negative” olarak başarıyla sınıflandırdığını göreceksiniz. Nötr veya karmaşık cümlelerde (örneğin “The movie was okay…”) modelin iki sınıftan birine yöneldiğini ancak güven oranının daha düşük kaldığını gözlemleyebilirsiniz. Bu durum, ikili (binary) sınıflandırma modellerinin doğal bir sınırıdır.
Duygu Analizinde Karşılaşılan Zorluklar ve Sınırlar
NLTK ve Naive Bayes ile geliştirdiğimiz bu model oldukça yüksek bir başarı gösterse de, gerçek dünya senaryolarında doğal dilin karmaşıklığından kaynaklanan bazı zorluklar mevcuttur:
- Alaycılık ve İroni (Sarcasm): “Harika, siparişim tam 3 hafta sonra kırık ulaştı!” cümlesi kelime düzeyinde “harika” gibi olumlu kelimeler içerse de aslında derin bir olumsuzluk taşır. Klasik kelime torbası (Bag of Words) modelleri bu ironiyi yakalamakta zorlanır.
- Bağlam Kaybı: Kelimelerin sırasını göz ardı ettiğimiz için “not bad” (kötü değil – olumlu) ifadesi, “not” ve “bad” kelimelerinin tekil olumsuz etkileri nedeniyle yanlış sınıflandırılabilir. Bu sorunu aşmak için n-gram (ikili veya üçlü kelime grupları) yaklaşımları kullanılabilir.
- Dil Desteği: Bu çalışmada kullanılan modeller ve WordNet veritabanı İngilizce odaklıdır. Türkçe gibi sondan eklemeli dillerde morfolojik analiz (kök bulma) çok daha karmaşık olup, Türkçe için geliştirilmiş özel kütüphanelerin (Zemberek gibi) veya Türkçe uyumlu stopword listelerinin kullanılması gerekir.
Sonuç ve İleri Adımlar
Bu makalede, Python 3 ve NLTK kütüphanesini kullanarak sıfırdan bir duygu analizi boru hattının (pipeline) nasıl kurulacağını öğrendik. Verilerin gürültülerden arındırılması, kelimelerin köklerine indirgenmesi, özellik çıkarımı yapılması ve Naive Bayes sınıflandırıcısı ile eğitilerek tahminler üretilmesi adımlarını pratik kod örnekleriyle uyguladık.
Kendinizi bu alanda daha da geliştirmek ve modelinizin başarısını artırmak için şu ileri seviye adımları deneyebilirsiniz:
- N-gram Modelleri: Sadece tekil kelimeleri (unigrams) değil, yan yana gelen ikili kelime gruplarını (bigrams) da modelinize öznitelik olarak ekleyin. Bu sayede “not good” gibi kalıpların anlamı korunur.
- TF-IDF (Term Frequency-Inverse Document Frequency): Kelimelerin sadece var olup olmadığını (True/False) değil, metin içerisindeki önem derecelerini ağırlıklandırarak modele aktarın.
- VADER Duygu Analizi: NLTK içerisinde yer alan ve özellikle sosyal medya dilleri için kural tabanlı olarak geliştirilmiş olan
SentimentIntensityAnalyzer(VADER) aracını araştırın. VADER, eğitim verisine ihtiyaç duymadan doğrudan duygu skorlaması yapabilmektedir. - Derin Öğrenme: Daha karmaşık metin yapıları için Word Embeddings (Word2Vec, GloVe) yöntemlerini ve LSTM veya Transformer tabanlı (BERT, RoBERTa) modern derin öğrenme modellerini inceleyin.
Doğal Dil İşleme, yapay zeka dünyasının en dinamik alanlarından biridir ve NLTK ile edindiğiniz bu temel bilgiler, daha karmaşık NLP projelerine geçiş yapabilmeniz için sağlam bir zemin oluşturacaktır.