Python NLTK İle Şiir Yazmak

Python nltk

Neden Python nltk ile şiir yazmak?

Öncelikle belirteyim, tecrübeli bir yazılımcı değilim ve hatta yazılımcı değilim:) Ancak yakında bir şiir kitabım çıkacak ve neden bir farklılık yapıp, Python NLTK (Natural Language Toolkit) ile şiir yazmak işine girişmiyorum dedim. Çünkü yaklaşık bir yıldır, düzenli bir şekilde Python çalışıyordum ve artık öğrendiklerimi bir proje ile pekiştirmek istiyordum. Yani aslında bu proje, öğrendiklerimi pekiştirmek için yaptığım deneysel bir çalışma. Başkalarına da faydası olur diye sayfamda da paylaşmak istedim.

Bu makaleyi yazarak, hem yaptıklarımı ayrıntılı bir şekilde kayıt etmeyi hem de benim gibi Python öğrenmekte olan arkadaşlara destek olmayı amaçlıyorum.

Bu kodu yazarken, birçok deneme yaptım. Yaklaşık beş tane sonuç veren örneğe ulaştım ve bu yazıda en çok hoşuma giden örneği anlatacağım. Bu arada, bir şeyi yeni öğrenirken sürekli denemenin önemli olduğuna inananlardanım. Öğrenme serüvenine devam eden tüm arkadaşlara da denemekten hiçbir zaman vazgeçmemelerini tavsiye ederim.

Programın önemli kısımları

Programı kısaca tanımlamak gerekirse, bir kaynaktan kelimeleri çekip 4 satırlık bir şiir veren bir Python NLTK çalışması. Bunun yanısıra, kullanıcıya dizelerin içinde geçmesini istediği kelimeleri soruyor. Eğer kullanıcı alt taraftaki kutulara kelime girerse, dizelere de bu kelimeleri barındıran satırları getiriyor.

Deneme yanılma sürecim boyunca biçimlenen programı, 3 ana bölüme ayırıyorum. Giriş bölümünde daha çok kaynak metini düzenledim. Bunu txt dosyası veya bir html adresini crawl ederek çekmek gibi iki yöntem uyguladım.

Uygulama kısmında ise, oluşturduğum bir arayüzün görsel özelliklerini ayarladım. Son olarak ise bence en kemik kısım yani işlem/fonksiyon kısmına yer verdim. Bu sebeple yazımda da aşağıdaki ana kısımların ayrıntılarını anlatacağım.

  • Giriş yani Kaynak Bölümü
  • Uygulama yani Kullanıcı Arayüzü
  • Fonksiyon yani İşlem Bölümü

Aşağıda her bölümü ayrıntıları ile paylaşıyorum. Ancak bu bölümleri açıklamadan önce program için indirdiğim kütüphaneleri de paylaşmam gerek. Bilenler bilir Python, bazı olmazsa olmaz kütüphanelere sahip bir dildir. Ben de ana hatlarıyla aşağıda paylaştığım kütüphaneleri kullandım.

Örneğin NLTK yani Natural Language Toolkit sayesinde kaynak seçtiğim (bu bazen bir internet sayfası oldu bazen de txt dosyası) metinlerden cümleleri seçip anlamlı satırlar elde etmeyi denedim. Burada söylemek lazım, python nltk başka dillerde kullanılabilir olmasına rağmen, Türkçe ‘de bence çok zayıf sonuç veriyor. İngilizce bir metinde bulunan özne, yüklem gibi öğeleri çok kuvvetli bir şekilde algılarken aynı durum Türkçe söz konusu olduğunda mümkün değil. Bu sebeple ben de python nltk kullanmama rağmen pos_tag gibi kuvvetli fonksiyonları kullanamadım.

Kaynak olarak http kullandığım durumlarda yardımcı olması için en ünlü kütüphanelerden biri olan requests kullandım.

Cümleleri rastlantısal olarak dizelere atamak için random ve tabi uygulamamı ve kullanıcı arayüzümü hazırlamak için de PyQt5 ve sys gibi kütüphanelere başvurdum. Kullandığım bütün kütüphaneleri programımın başında bulabilirsin.

Kullandığım bütün kütüphaneler aşağıdaki gibi.

import random
import re
import string
import nltk
from collections import defaultdict
from nltk import pos_tag # for nltk pos_tag
import requests # for beautifulsoup in case of crawling
from nltk.probability import FreqDist
from bs4 import BeautifulSoup # for crawling
import codecs
from collections import Counter
from nltk.util import ngrams
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5 import QtCore
import sys
from PyQt5.QtWidgets import QApplication # console
from pyqtconsole.console import PythonConsole # console
from PyQt5 import QtWidgets

Giriş yani Kaynak Bölümü

Öncelikle bir cümle havuzuna ihtiyacım oldu. Bu cümle havuzundan istediğim kelimeleri ayırıp listelere atabilecektim. Örneğin içinde binlerce kelime olan bir kelime havuzuna, bana son 3 harfi -mek, -mak olan kelimeleri ver dediğinizde %95’lik bir sonuçla yüklemlere ulaşabiliyordum.

Şiir yazmak fikri ilk aklıma geldiğinde, rastlantısal olarak seçtiğim bir internet sayfasından kelimeleri seçmek ve bu kelimelerle özne, yüklem gibi cümle öğelerini ayırmak gelmişti. Bu sebeple kaynak olarak bir internet sayfasını kullanabilir veya kendi metinlerimden oluşan ve yeterince kelimeye sahip bir .txt dosyasını kullanabilirdim. Her iki durumda da bir cümle havuzuna ihtiyacım olacaktı. Aşağıdaki satırlarla sentencepool yani cümle havuzuna ulaşabiliyordum. Daha sonra Python nltk ile bu cümle havuzu ile istediğim gibi oynayabildim.

sentencepool = codecs.open(“C:/Users/ceker/Desktop/kitap.txt”, “r” , encoding=’utf-8′).read()

Veya cümle havuzunu bir internet sayfasından çekmek istersen aşağıdaki satırlar işine yarayabilir.

url = “https://www.harbiben.com/eski-gunleri-ozlemek/” art = Article(url) sentencepool = art.text words = nltk.word_tokenize(sentencepool)

Böylece elimde istediğim gibi işleyebileceğim bir cümle havuzum oldu. Sıra bu kaynaktan anlamlı cümleler elde edebilmeye geldi. Bu amaçla ilk olarak özneler, yüklemler ve tümleçlerden oluşan listeler oluşturup. Buradan anlamlı cümleler elde etmeyi denedim Ne de olsa lisan matematiktir:)

Ancak en iyi sonuç aldığım yöntem bigram ve multigram oldu. Bigramlar kaynak cümleleri ikli kelimelere ayırırken, multigramlar ise n sayıda kelimeden oluşan anlamlı veya anlamsız cümleler oluşturuyor.

Örneğin, ‘Uzak yollardan düştüm karşına’ gibi bir cümleden n=3 olduğunda ‘Uzak yollardan düştüm’ ve ‘yollardan düştüm karşına’ şeklinde iki cümle elde edilebiliyor. Burada belirtmem gerekir ki, şu ana kadar anlamlı veya anlamsız tüm cümleleri topluyoruz. Filtreleme işlemeni daha sonra anlatacağım.

Bu bilgi ile yola çıkarak, aşağıdaki satırlarla wordquantity yani n=3 olacak şekilde 3 kelimeli multigram veya iki kelimeli bigram listesi oluşturdum.

wordquantity=3
multigram = list(nltk.ngrams(sentencepool.split(),wordquantity))
bigram = list (nltk.bigrams(sentencepool.split()))

Uygulama yani Kullanıcı Arayüzü

Uygulama kısmı aslında, kullanıcı için rahat bir arayüz sunmak ve arka plandaki fonksiyonu çağırmak gibi bir işleve sahip. O sebeple kullandığım öğeleri anlatacağım ancak bütün kodu irdelemeni ve ayrıntılarına bakmanı tavsiye ederim.

Öncelikle her uygulamada olduğu gibi aşağıdaki satırlara ‘Window’ isimli bir pencere (Class olarak) oluşturdum.

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setUI()

Sonraki kısım ise tamamen uygulama arayüzünün görselliği ile ilgili. Örneğin aşağıdaki satır ile pencerenin arkaplanını rgb kodu vererek renklendirebilirim.

self.setStyleSheet("background-color:rgb(144,166,150)")

Daha sonra fonksiyonu çağıracak bir butona ihtiyacım var. Aşağıda bulunan ilk satır ile bu butonu oluşturuyorum. İkinci satır ise, bu butona tıklandığında, şiir yazan fonksiyonu çağırmak için.

self.button = QPushButton("Yazdır/Write...")
self.button.clicked.connect(self.printpoem)

Butonu oluşturduktan sonra ise şiirin yazılacağı satırları oluşturmam gerek. Bu sebeple aşağıdaki satırlarda bu alanı oluşturmuş oluyorum.

self.qle1 = QLineEdit()
self.qle1.setStyleSheet("border: 0px solid red;")
self.qle2 = QLineEdit()
self.qle2.setStyleSheet("border: 0px solid red;")
self.qle3 = QLineEdit()
self.qle3.setStyleSheet("border: 0px solid red;")
self.qle4 = QLineEdit()
self.qle4.setStyleSheet("border: 0px solid red;")

İhtiyacım olan bir diğer alan da kullanıcıdan isteyeceğim input kelimelerini yazdırmak için gerekli olan girdi alanları. Bu sebeple aşağıdaki satırlarla bu alanı oluşturuyorum. Daha sonra fonksiyon kısmında anlatacağım gibi, bu alana girilen kelimeleri değişkenlere atayacağım.

self.qte1 = QTextEdit()
self.qte1.setMaximumSize(90,30)
self.qte2 = QTextEdit()
self.qte2.setMaximumSize(90, 30)
self.qte3 = QTextEdit()
self.qte3.setMaximumSize(90, 30)
self.qte4 = QTextEdit()
self.qte4.setMaximumSize(90, 30)

Sonrasında ise QHBoxLayout() ve QVBoxLayout() gibi işlemlerle tamamen layout ayarlaması yapıyorum.

Ayrıca kullanıcıyı bilgilendirmek için aşağıdaki satırları kullanarak bir bilgi alanı oluşturabilirsin.

self.infolabel = QtWidgets.QLabel()
self.infolabel.setText("eğer istersen mısralar için kelime seçebilirsin")

self.infolabeleng = QtWidgets.QLabel()
self.infolabeleng.setText("you can choose also a word from the list")

Fonksiyon yani İşlem Bölümü

Aslında programın iş yapan kısmı bu bölüm. Dikkat edersen buraya kadar Python nltk haricinde pek bir şey yapmadık. Şimdi artık oluşturduğumuz cümle havuzu ile oynama vakti geldi. Yani hiç arayüze yansıtmadan, sadece bu kodu çalıştırınca bile aynı çıktıyı veriyor. Fonksiyonun ilk satırları ise aşağıdaki gibi.

input1list = []
input1 = str(self.qte1.toPlainText())

input2list = []
input2 = str(self.qte2.toPlainText())

input3list = []
input3 = str(self.qte3.toPlainText())

input4list = []
input4 = str(self.qte4.toPlainText())

Fonksiyona gelince, her dize için inputlist isimli boş bir liste oluşturuyorum. Örneğin birinci dizenin boş listesi input1list [] şeklinde tanımlı. bu boş listeye daha sonra istediğim gibi satırları göndereceğim.

Sonrasında gelen örneğin ‘input4 = str(self.qte4.toPlainText())‘ satırı ile de uygulama da bulunan girdi alanlarından alacağım kelimeleri input4 isimli değişkene aktarıyorum. Yani kullanıcı, arayüzde eğer isterse, bir kelime seçiyor ve ben de daha önce oluşturduğum listeden içinde bu kelimeyi barındıran dizeler seçiyorum. Eğer bu değişken boş ise, program bu durumda herhangi bir dize seçmekte. Başka bir varyasyonda ComboBox ile, kullanıcıya seçenek listesi de vermek mümkün. Bu değişkeni daha sonraki satırlarda kullanacağım.

For Döngüsü

Programın sonraki satırları da for düngüsü ile, inputları içinde barındıran dizeleri seçme işini yapıyor. Bu arada c!=d != y satırı ile de (!= >>eşit değildir demek) kelimelerin birbiri ile aynı olmamasını şartlıyoruz.

for (c, d, y) in multigram:
    c != d != y
    if input1 in (c,d,y):
        input1list.append(c+ " " + d + " " + y)

    if input2 in (c,d,y):
        input2list.append(c + " " + d + " " + y)

    if input3 in (c,d,y):
        input3list.append(c + " " + d + " " + y)

    if input4 in (c,d,y):
        input4list.append(c + " " + d + " " + y)

Listeler hazır olduktan sonra, içinde kullanıcı tarafından seçilen kelimelerin bulunduğu dizelerden oluşan bu listelerden, rastlantısal olarak bir tanesini seçip dizeleri oluşturuyorum. Bu rastlantısal seçimi yapan kod ise aşağıdaki gibi.

row1 = input1list[random.randint(0, len(input1list) - 1)]
row2 = input2list[random.randint(0, len(input2list) - 1)]
row3 = input3list[random.randint(0, len(input3list) - 1)]
row4 = input4list[random.randint(0, len(input4list) - 1)]

Dizeler oluştuktan sonra ise bu dizeleri sadece konsolda değil de aynı zamanda uygulamadaki satırlarda da görmek için son olarak aşağıdaki satırlara ihtiyacın var. Bu sayede uygulamada yer alan satırlara setText komutu ile, dizeleri rastlantısal atadığın değişkenleri yazdırabiliyorum.

self.qle1.setText(row1)
self.qle2.setText(row2)
self.qle3.setText(row3)
self.qle4.setText(row4)

İlk projemi okuyup, heyecanıma ortak olduğun için teşekkür ederim. Eğer bir sorun olursa her zaman yorumlara yazabilirsin. Sana da öğrenme serüveninde başarılar dilerim…

Ayrıca diğer denediğim kodları da seve seve paylaşabilirim. Bunun için bana mail atman yeterli.

Application

Arayüz (PyQt5) için bazı ince ayarlar

Açıkçası daha önce dizayn kısmını kolay sanmıştım. Hatta Qt Designer ile kısa sürede hallederim diye düşünmüştüm. Ancak işe girişince pek de öyle olmuyormuş. Yani hangi buton nerede olmalı, renk seçimi nasıl olmalı gibi daha birçok ayrıntı ile de ilgilenmem gerekti.

Bu aşamada ben de Qt Designer yerine daha öok şey öğrenmek için dizayn kısmını da üstlenmek istedim.

Aşağıda sana kendi çalışmam sırasında araştırarak edindiğim bazı kodları yazıyorum.

Örneğin QTextEdit() alanlarına gölgelendirme yapmak istediğimde aşağıdaki satırlara ihtiyaç duydum. İlk iki satırda shadowqte1 gibi bir efekt tanımladım ve üçüncü satırda da bu efekti istediğim alana delege etmiş oldum.

shadowqte1 = QGraphicsDropShadowEffect()
shadowqte1.setBlurRadius(15)
self.qte1.setGraphicsEffect(shadowqte1)

Bunun yanı sıra Placeholder özelliği de gayet faydalı. Eğer kullanıcıdan bir bilgi girmesini istiyorsan, bu kod sayesinde girdi alanı otomatik olarak saydam oluyor ve daha da iyisi, kullanıcı bu alana tıkladığında yazılı metin silinerek yeni girilecek metin bu alana yazılıyor.

self.qte1.setPlaceholderText("enter word1 /   bir kelime gir")

Ayrıca her türlü alanın en ve boy ayarını aşağıdaki örnek satırlarda olduğu gibi yapabilirsin. Eğer renk, çerçeve ve yazı tipini de ayarlamak istesen setStyleSheet komutu sana yardımcı olacaktır.

self.qte1.setMaximumSize(110,50)
self.qte1.setStyleSheet("border: 0.5px solid #85797b; font-size:12pt; font:bold; background-color:rgb(212, 201, 203)")

Son olarak eğer uygulamana bir resim eklemek istersen aşağıdaki satırlardan yararlanabilirsin. Benim uygulamamdaki martı resmini de bu yolla ekledim.

self.labelimage = QLabel()
self.pixmap = QPixmap("...Desktop/marti.png")
self.labelimage.setPixmap(self.pixmap)
v_box.addWidget(self.labelimage)
self.labelimage.setContentsMargins(300, 0, 0, 0)

Python NLTK ile şiir yazmak projemden öğrendiklerim

Yazımın girişinde de bahsettiğim gibi, bu projedeki asıl amacım öğrendiklerimi pekiştirmek ve benim durumunda olan arkadaşlara bir örnek sunmak. Ben de bu proje sırasında öğrendiklerimi aşağıdaki gibi sıralayabilirim.

  • Yapmak istediklerini erteleme. Basit de olsa başlamak, bitirmenin yarısıdır.
  • Öğrenmenin yaşı ve sınırı yoktur. Önemli olan sürekli şekilde öğrenmek.
  • Data yani veri, algoritmadan daha önemli bir yere sahip. O sebeple, bence önümüzdeki dönemde data işlemek çok önemli bir yere sahip olacak.
  • Yapay zeka daha yolun başında. Ancak geleceğe dair çok umut verici.
  • Yeni şeyler denemek için vaktin yoksa, vakit yaratabilirsin.

Paylaştığım daha renkli makaleler için de bu linki tıklaman yeterli.

1 Trackback / Pingback

  1. Denize Kalan Şiir Kitabım - HARBİBEN

Leave a Reply

Your email address will not be published.


*