Python ile Film Yorumlarının Duygu Analizi

Kübra Kurt
6 min readFeb 17, 2021

Duygu Analizi asıl olarak, insani duyguları makineye öğretmeyi amaçlamaktadır. Çoğunlukla pozitif, negatif ve nötr olarak sınıflandırma yapılır. Fakat başka birçok çeşitleri de mevcuttur. Mesela mutlu, üzgün, şaşkın, kızgın vb. şeklinde de sınıflandırma yapılabilir.

Son zamanlarda çok fazla önem kazanan bir alan oldu. Özellikle pandemi süreci içerisinde yorumların önemi kat kat arttı. İnsanlar sosyal medya, film/dizi platformları, e-ticaret vb. sitelerde oldukça fazla vakit geçirmeye başladı. Bunlardan birisi için duygu analizi örneği verecek olursak; e-ticaret sitelerinde herhangi bir ürünün alışverişinden sonra yapılan yorumlar alıcının o ürün hakkındaki duygularını, tutumunu belirlemeye fayda sağlar. Bu sayede satıcı müşteri tutumunu tespit edip yapılması gereken herhangi bir şey varsa yapmak ya da düzeltilmesi gereken herhangi bir durum varsa düzeltmek konusunda girişimlerde bulunur. Aynı zamanda bahsettiğimiz üzere özellikle pandemi sürecinde diğer alıcı adaylara büyük fayda sağlamıştır. Çünkü müşteriler ürünü canlı göremiyorlar ve bu yüzden de tereddüt içerisinde kalıyorlar. Mesela kıyafet alırken tam olarak beden yapısı hakkında öngörüşlü olamıyorlar ya da ev eşyası alacaklarsa kalitesi hakkında bir çıkarımda bulunamıyorlar. Bu yorumlar sayesinde ürünün kalitesi, tipi, yapısı, faydası, fiyatı vb. özellikleri hakkında fikir edinip ürünü alıp almamaları konusunda daha kolay karara varabiliyorlar. Bu da yine satıcının satış miktarını büyük oranda etkilemektedir.

Kaynak

Film Yorumlarının Duygu Analizi

Kaggle üzerinden aldığım veri seti içerisinde 83227 gözlem ve 3 değişken mevcuttur. Bu veri; film yorumları (comments), film adları (film_name) ve yorumların duygu derecelerinden (points) oluşmaktadır.

Öncelikle veriyi ön işlemlerden geçirip modele uygun hale getireceğiz ve ardından duygu analizini gerçekleştireceğiz.

Gerekli kütüphaneleri import edelim.

Veri setimizi okutalım.

Görüldüğü üzere yorumların hepsinin başında “\n” böyle bir karakter mevcuttur. Biz de bir fonksiyon yazarak bu karakterleri kaldıracağız.

Birkaç satır ile kontrol ettiğimde verinin başından 23 karakter ileri, sonundan da 24 karakter geri gittiğimde oluşmuş boşluklar ve “\n” karakterleri ortadan kalkıyor. O yüzden bunu tüm veriye uygulayabileceğimiz bir fonksiyon yazmamız gerekiyor.

Öncelikle comments adlı bir fonksiyon oluşturuyorum. Bu fonksiyon istediğimiz değişken içerisindeki gözlemlerin başından 23, sonundan 24 karakter kaldıracak. Ardından bu fonksiyonu comment değişkenindeki gözlemlere apply modülü ile tek tek uyguluyorum ve tekrar aynı değişken içerisine atıyorum.

Filmlere 1–5 arası puanlandırma yapılmıştır. Bizim duygu sınıflandırmamız pozitif ve negatif olarak ayrılacağı için bunu ikili sınıflandırmaya dönüştürmem gerekiyor. Fakat ondan önce bir sorun daha mevcut, veri içerisinde numaralar virgül ile ayrılmış. Biz sadece ilk karakteri alacağız ve ardından float tipine dönüştüreceğiz.

Bir önceki kodda yaptığımıza benzer floatize fonksiyonu oluşturuyoruz ve point içerisindeki gözlemleri apply modülünü kullanarak düzeltiyoruz.

Düzelttiğimiz puanları, 0 ve 1 olarak sınıflandıracağız. 3 puanı bizim için nötr oluyor. Ne pozitif ne de negatif diyebiliyoruz. Bu satırları veriden çıkaracağız. Ardından 3'ten küçüklere 0 (negatif), 3'ten büyüklere 1 (pozitif) diye atama yapacağız.

Veri içerisinden sildiğimiz satırlar olduğu için index yapısı bozuldu. İndexleri resetliyoruz.

Yorumların ön işlemlerine geçiş yapıyoruz. İlk önce comments altındaki bütün karakterleri küçültüyoruz. Bunu lower modülü ile gerçekleştireceğiz. Apply modülü ile tüm veri içerisine uyguluyoruz.

Şimdi ise noktalama işaretlerini kaldıracağız. remove_punctuation adlı bir fonksiyon yazıyoruz. Bu fonksiyon yorumlar içerisinde dolanacak ve eğer gezindiği karakter noktalama işareti değil ise bunu word_wo_punc değişkenine atacak. Ardından da biz bu apply modülü ile bunu veriye uygulayacağız ve bize veriyi noktalama işaretleri kaldırılmış bir şekilde verecek. Fakat bunu tek başına yaptığımızda içerisinde hala kalkmamış olan “\n” ve “\r” karakterleri yer alıyor. Bunu da kendimiz manuel olarak değiştiriyoruz.

Sırada numerik karakterleri kaldırmamız gerekiyor. Bunun içinde remove_numeric isimli bir fonksiyon yazıyoruz. Bu fonksiyon da yorumlar içerisinde dolanacak ve numerik olmayan her şeyi output değişkeninin içerisine atacak. Ardından bize de numerik karakterler kaldırılmış olarak bir çıktı verecek.

Artık modeli kurmak için girişimlerde bulunabiliriz. Etiketleri (points) ve yorumları (comments) ayırarak listeler içerisine alıyoruz.

Elimizdeki verilerin %80'i ile modeli kurup, %20'si ile modelimizi test edeceğiz. Bu sebepten dolayı öncelikle verinin boyutunun %80'inini alarak cutoff değişkenine atıyoruz. Ardından da cutoff değişkenine göre oluşturduğumuz target ve data listelerini, train ve test olarak ayırıyoruz.

Artık elimizdekileri tokenleştirebiliriz. Tokenleştirme işlemi yapılırken her yorum kelimelere ayrılacak. Kelime haznesindeki her kelimeye karşılık farklı bir sayı gelecek. İlk önce kelime haznemizde en fazla kaç tane kelime oluşsun bunu belirleyeceğiz. Bunun için num_words değişkenine 10.000 değerini veriyoruz. Bununla beraber en sık geçen 10.000 kelimeyi alacağız. Duygu Analizi yapacağımız için veri içerisindeki tüm kelimeleri almamıza gerek yok. Tokenleştirme işlemini “keras” kullanarak gerçekleştiriyoruz.

Tokenizer tanımladığımıza göre elimizdeki veriyi tokenleştirebiliriz.

Train ve test verisindeki tüm yorumları tokenler halinde bir değişken içerisinde saklayalım. Ardından da 1000. satırın nasıl gözüktüğünü kontrol edelim.

Kelime haznesini sınırlandırdığımız için her kelimeye karşılık bir token bulunmayacak. Eğer kelime haznesinde değilse o kelime, yok sayılayacak.

Sinir ağlarında genellikle RNN ile oluşturduğumuz modellere belli boyutlarda inputlar veririz. Bizim verideki yorumlar hepsi farklı sayılardaki kelimelerden oluşmaktadır. Farklı sayılardaki kelimeleri RNN içerisine input olarak veremeyiz. Tüm yorumları aynı boyuta getirmemiz gerekir. Eğer yorum belirlediğimiz boyuttan düşük boyutta olursa 0 eklenecek. Eğer yorum belirlediğimiz boyuttan yüksekse belirli kısımları silerek kendi belirlediğimiz boyuta getireceğiz.

Öncelikle for döngüsü oluşturup veri setimizdeki her yorumun üzerinden tek tek geçeceğiz ve token sayısını alacağız. Liste üzerinde işlem yapmayı kolaylaştırmak amacıyla da listeyi numpy array’e dönüştüreceğiz.

Ortalama olarak bir yorumda kaç tane token olduğuna bakalım.

En fazla token bulunan yorumun token sayısına bakalım.

Şimdi yorumların boyutlarını eşitleyeceğiz. Bunun için kendimiz bir boyut belirliyoruz. Kendimiz değer verebiliriz, en yüksek tokenli yorumun boyutunu kullanabiliriz ya da ortalamayı alabiliriz. Biz ise ortalamayı alıp 2 standart sapma ekleyerek elde edeceğiz.

Elde ettiğimiz boyut yorumların yüzde kaçını kapsıyor bakalım.

Train setindeki her yorumun boyutunu elde ettiğimiz boyuta getiriyoruz. İlk önce padding eklenecek veriyi giriyoruz. Ardından belirlediğimiz boyutu giriyoruz.

Padding’i daha iyi görebilmek 800. yoruma bakalım.

Aynı yorumun padding uygulanmış haline bakalım.

Tokenleri verip, stringi alabilmek için fonksiyon yazacağız. Bu şekilde elimizdeki tokenleri tekrar yoruma çevireceğiz. word_index içerisinde sözlük olarak kelimeler, kelimelerin sayısal olarak tokenleri bulunuyordu. word_index ile kelimeler ve sayıların yerini tersine çeviriyoruz. Artık bir sayıyı verdiğimiz zaman o sayıya karşılık gelen kelimeyi bulabileceğiz. Fonksiyon içerisinde sıfırlar kelimeye karşılık gelmediği için dahil etmiyoruz. Diğerlerini de liste içerisinde topluyoruz. Son olarak da bize yorumu çıktı olarak verecek.

Sıra modeli kurmaya geldi. Embedding matrisini oluşturmadan önce, embedding matrisinin büyüklüğünü belirliyoruz. Her kelimeye karşılık gelen 50 uzunluğunda vektör oluşturuyoruz. Modele bir şey ekleyebilmek model.add kullanıyoruz.

num_words (10 bin) kelime sayılarıdır. 10 bin kelimeye karşılık gelen 50 uzunluğunda rastgele vektörler oluşturalım. Layer input aldığı zaman, input içerisindeki kelimelerin vektörlerini bir sonraki layer’a gönderecektir. embedding_layer’ın output’u, bir sonraki layer’ın input’u olacak.

Yinelenen sinir ağımızı (RNN) oluşturuyoruz. Yinelenen sinir ağını oluşturmak GRU kullanacağız. model.add(GRU()) bu şekilde de kullanılabilir. units, GRU değerimizin nöron sayısıdır. Bu layer’da 16 tane output verilecek. return_sequence = True, output olarak sequences tamamı döndürülecek. Eğer False olsaydı sadece son output döndürülürdü. Bir sonraki layer’a GRU ekleyeceğimiz için buna True demek zorundayız. Eğer bir sonraki layer bir nörondan oluşsaydı False girerdik. Sigmoid ile output’u [0,1] arasına sıkıştırıyoruz.

Modeli kurduk. Modelin eğitimine dair optimizasyon algoritması belirleyeceğiz. Bunun için Adam algoritmasını kullanacağız.

Optimizasyon algoritmasını belirledikten sonra, modelimizi derliyoruz. Sadece iki sınıf olduğu için de “binary_crossentropy” kullanacağız.

Eğitime başlıyoruz. Eğitimden kaç defa geçeceğine epochs parametresi ile karar veriyoruz.

Test setindeki ilk 1000 yorum için tahminde bulunuyoruz. Bu tahminler bir matrisin sütunu olarak atanacaktır. Her satırda bir yorumun tahmin değeri bulunacaktır. Bu elemanlarla işlem yapmayı kolaylaştırabilmek için sütunu satıra çevireceğiz. Böylece elimizde tahmin değerlerini gösterecek bir vektör elde edeceğiz. Vektör üzerinde işlem yapmak daha kolaydır. Sütunu satıra çevirebilmemiz için matrisin transpozunu alıyoruz.

Elimizdeki tahmin değerlerini gerçekleri ile karşılaştırarak hangi yorumlarda yanlış olduğunu göreceğiz. Elimizdeki tahmin değerleri 0.5'ten büyükse 1, küçükse 0 yapacağız. Böylece 0 ve 1'lerden oluşan bir vektör elde edeceğiz. Elde ettiğimiz vektörü etiketlerle karşılaştıracağız.

Elimizdeki iki vektörü karşılaştıracağız. Yanlış tahminlerin indexlerini alalım.

1000 tane yorumdan kaç tanesinin yanlış tahmin edildiğine bakalım.

Yanlış bilinen yorumlardan bir yorumun indexini alarak yoruma bakalım.

Modelimizin bu yoruma nasıl tahminde bulunduğuna bakalım.

Modelimiz yorumu olumlu tahmin etmiş, doğrusuna bakalım.

--

--

No responses yet