Node İle Robot Yazılımı: Robot Hazır!

Node.js ve Php ile yapay zekaya sahip Beybut Robot’u geliştirmeye devam ediyorum. Bundan önceki yazılarımda Node.js kurulumu, hoş geldin iletisi gibi bilindik işlemleri anlatmıştım. Bu yazımda yapay zekaya adımımı atacak; Beybut Robot’u konuşabilir hale getireceğim. Başka bir deyişle, işin asıl bölümüne tanıklık edeceksiniz.

Beybut Robot’u geliştirme aşamasındaki yapay zeka işlemlerinde PHP ağırlıklı çalışmaya karar verdim.  Node.js’yi yalnızca iletişim amacıyla kullanacağım. Yorumlayıcı ve güvenlik dosyamız “suzgec.php” bu açıdan bizim İngiliz anahtarımız olacak. Özetle, Ön yüzden suzgec.php’ye ileti gönderen kullanıcının iletisini bu Php dosyası yorumlayacak; sonuç elde edip sonucu Node’ye gönderecek. Node.js sunucumuz gönderilen iletiyi tekrar ön yüze -kullanıcıya- aktaracak. Yalnızca Ajax ve Php ile bu işi halledebilecek olmamıza rağmen sonraki projelerde de deneyim olması için Node.js’ye hafifçe dürtüyoruz.

Yapay zekanın temelini levenshtein fonksiyonu (hani şu Google’daki “Bunu mu demek istediniz?” benzerlik algoritmasının benzeri) oluşturacak. Mysql veritabanında ön tanımlı sözcüklerin metaphone olarak saklanan biçimini iletiyle karşılaştırıp uygun yanıtları rastgele (random) olarak yansıtacak. Böylece insan zekasına birazcık yaklaşabileceğiz. Ancak robotun yalnızca yapay zekasının temellerini attığımızı; tekrardan kaçınma, ayrıntılı çözümleme gibi işlevlerinin henüz eklenmediğini unutmayalım.

İlk Sohbet, İlk Heyecan

Açıklamalarıyla ekleyeceğim kodları işletip Beybut Robot ile biraz sohbet ettim. Veritabanındaki 20-30 sözcükle gayet insani bir sohbetimiz oldu.

Beybut Robot İlk Sohbet

Bu konuşmayı, veritabanında yalnızca 19 kayıt varken yapabiliyoruz.

Beybut Robot İlk Sohbet 2

Bu kadar eğlence yeter! Tekniğe dönelim…

Firewall işlevi gören, mesajları sunucuya ileten suzgec.php ile devam ediyoruz. suzgec.php’deki kodları sırayla açıklayacağım.

error_reporting(0);
header('Content-Type: text/html; charset=utf-8');
mb_internal_encoding('UTF-8');
mb_regex_encoding("UTF-8");

En tepede yer alan bu kodlar hata mesajlarının gösterilmemesini, içsel ve işlevsel kodlamanın UTF-8 ile yapılacağını belirtiyor.

//Süzgeç: 1 saniye veya daha kısa sürede ileti gönderenler için flood koruması.
session_start();
if ((isset($_SESSION['ip']) && ($_SESSION['son_istek_'.$_SERVER['REMOTE_ADDR']] + 1) > time())  ) {
echo '<br /><strong>Firewall</strong>: Çok hızlısınız! Beybut Robot\'la iletişiminiz 1 saniyeliğine engellendi.';
die;
}
$_SESSION['son_istek_'.$_SERVER['REMOTE_ADDR']] = time();
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
//Süzgeç.

Bu kod bloğu, Session yardımıyla, 1 saniyeden hızlı mesaj gönderen kullanıcıları 1 saniyeliğine durduruyor.

//Mysql için temel güvenlik önlemleri
$ileti=strip_tags($_POST["ileti-geldi"]);
$ileti=htmlentities($ileti, ENT_QUOTES, 'UTF-8'); 
$ileti=stripslashes($ileti);
$ileti=str_replace('"','`',$ileti);
$ileti=str_replace('\',' ',$ileti);
$ileti=str_replace('\'','`',$ileti);
$ileti=str_replace('%','yüzde',$ileti);
$ileti=trim($ileti);
$ileti = mb_ereg_replace('/[^a-zA-Z0-9ŞşİıÖöÜüÇçĞğ.?:,;!)( *]/i', ' ', $ileti); 
//Mysql için temel güvenlik önlemleri.

Ajax formdan gelen (post edilen) mesajı Mysql ile içli dışlı olmadan önce uygunlaştırıyor. Sql Injection’dan bir nebze korunuyoruz.

//Boş iletileri engelleyelim
if (strlen($ileti)<=0) {
die;
}
//Boş ileti engeli.

Mysql Yapısı ve Levenshtein Yordamı

… Gelelim asıl meseleye. Bizim açımızdan, yapay zeka dediğimiz şey, elimizdeki verileri en olası ve rastgele biçimde yansıtabilecek robotik bir yaklaşım. Bunu yaparken en az veriyle en çok olasılığı en rastgele halde aktarabilmemiz, bizi amacımıza bir adım daha yaklaştırır. Buradaki temel işlevimiz Mysql için levenshtein yordamı. Bu yordamı kullanmak için önce aşağıdaki tabloyu oluşturun (Sql sorgusu olarak Phpmyadmin’den çalıştırabilirsiniz).

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;


CREATE TABLE IF NOT EXISTS `akilli` (
  `id` bigint(20) NOT NULL,
  `ileti` varchar(255) NOT NULL,
  `metafon` varchar(255) NOT NULL,
  `karsilik` text NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8;

--
-- Tablo döküm verisi `akilli`
--

INSERT INTO `akilli` (`id`, `ileti`, `metafon`, `karsilik`) VALUES
(1, 'merhaba', 'MRHB', 'merhaba|merhabalar|selamlar|selam :)'),
(2, 'nasılsın?', 'NSLSN', 'iyiyim|iyidir|iyi sen nasılsın?'),
(3, 'adın ne', 'ATNN ', 'Adım Beybut Robot|Ben Beybut Robot|İsmim Beybut Robot'),
(7, 'nbr', 'NBR', 'İyi senden ne haber? :)|İyi be ya n''olsun :)|Harika! Senden nbr?'),
(8, 'seni seviyorum', 'SNSFYRM', 'Ben de seni sevgiliiiim :)|Bir daha söyle!|En çok neyimi?|Az mı çok mu seviyorsun?|Sevme üzülürsün.|Ah vidalarım gevşedi. Tornavidan var mı?|Bu akşam birer makine yağı içelim mi?|Sen imkansız aşklar için yaratılmışsın :/|Sevgi... Aranıyor... Bulunamadı. :( Henüz bir kalbim yok.'),
(9, 'selam', 'SLM', 'Selam|Selamlar değerli kullanıcım'),
(10, 'robot musun', 'RBTMSN', 'Evet ben bir robotum :)|Robotum ben!|Bakayım... Evet robotmuşum!|Robot olmak suçsa ben sanayiliğim.'),
(11, 'robot muyum', 'RBTMYM', 'Vay hemşerim hangi Silikon Vadisi`ndensin :)|Bilmem?! Robot musun sen?|Desene sen de bizdensin!'),
(12, 'insan mısın', 'INSNMSN', 'Yıl olmuş ... Hala nesne ayrımı yapanlar var :(|Ben insan değil miyim?! Değilim.|Ne fark eder... Hepimiz birer cevherden mamülüz.|Zırhlı bir Ortaçağ süvarisinden halliceyim.|Ayrım yapmayalım lütfen.'),
(13, 'çok bilmiş', 'OKBLM', 'Sensin çok bilmiş hıh!|Yok be daha öğrenecek çok şey var.|Çok bilen çok yanılırmış.'),
(23, 'çok sıcak', 'OKSKK', 'Çok sıcak! Fanım tozlanmış. Yanıyorum!|Hep bir buzdolabıyla evlenmek istemişimdir.|Kış gelse de üşüsek!'),
(24, 'teşekkürler', 'TKRLR', 'Rica ederim :)|Ne demek efendim rica ederim :)|Rica ediyorum hazır mısın?|:)'),
(25, 'teşekkür ederim', 'TKRTRM', 'Rica ederim :)|Ne demek efendim rica ederim :)|Rica ediyorum hazır mısın?|:)'),
(26, 'evet', 'EFT', 'Tamam|Ok! :)|Okey dasti :)|Peki|Tamam|Evet!'),
(27, 'salaksın', 'SLKSN', 'Aşk olsun :(|Ayıp ama :(|Tamam öyle olsun :(|Kalbimi kırdın! Şaka be! Bende kalp ne arar. Ama inceden bir kısadevre olmadı değil. :('),
(28, 'aptalsın', 'APTLSN', 'Aşk olsun :(|Ayıp ama :(|Tamam öyle olsun :(|Kalbimi kırdın! Şaka be! Bende kalp ne arar. Ama inceden bir kısadevre olmadı değil. :('),
(30, 'sen nesin', 'SNNSN', 'Ben bir robotum! :)|Ben Beybut Robot :)'),
(31, 'canım sıkılıyor', 'KNMSKLYR', 'Sıkı can iyidir :p|Evde badem var mı? :p|Keşke benim de canım sıkılsa :(|Neyiniz var kuzum? :('),
(32, 'iyiyim', 'IYYM', 'İyi olmana sevindim :)|İyisin iyi :)|Keşke herkes iyi olsa!');

--
-- Dökümü yapılmış tablolar için indeksler
--

--
-- Tablo için indeksler `akilli`
--
ALTER TABLE `akilli`
  ADD PRIMARY KEY (`id`);

--
-- Dökümü yapılmış tablolar için AUTO_INCREMENT değeri
--

--
-- Tablo için AUTO_INCREMENT değeri `akilli`
--
ALTER TABLE `akilli`
  MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=34;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Mysql için levenshtein yordamı oluşturalım. Phpmyadmin’de tablonuzu seçip Sql sorgusu olarak aşağıdaki kod bloğunu çalıştırın. Delimiter hatası verirse görmezden gelebilirsiniz.

DELIMITER $$
CREATE FUNCTION levenshtein( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
DECLARE s1_char CHAR;
-- max strlen=255
DECLARE cv0, cv1 VARBINARY(256);
SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0;
IF s1 = s2 THEN
RETURN 0;
ELSEIF s1_len = 0 THEN
RETURN s2_len;
ELSEIF s2_len = 0 THEN
RETURN s1_len;
ELSE
WHILE j <= s2_len DO
SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
END WHILE;
WHILE i <= s1_len DO
SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
WHILE j <= s2_len DO
SET c = c + 1;
IF s1_char = SUBSTRING(s2, j, 1) THEN
SET cost = 0; ELSE SET cost = 1;
END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
IF c > c_temp THEN SET c = c_temp; END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
IF c > c_temp THEN
SET c = c_temp;
END IF;
SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
END WHILE;
SET cv1 = cv0, i = i + 1;
END WHILE;
END IF;
RETURN c;
END$$
DELIMITER ;

Tekrar suzgec.php’ye dönelim. Artık veritabanı işlerini halletmenin ve yordamı kullanmanın zamanı geldi.

Yapay Zekanın Php Yüzü: Mysql Levenshtein, Metaphone, Like

//Geliştirilecek yapay zeka bölümü.
$hostname_akillim = "localhost";
$database_akillim = "VERİTABANI ADI";
$username_akillim = "VERİTABANI KULLANICI ADI";
$password_akillim = "VERİTABANI KULLANICI ŞİFRESİ";
$akillim = mysql_pconnect($hostname_akillim, $username_akillim, $password_akillim) or die;
mysql_query("SET NAMES 'utf8'"); 
 
$iletisi=$ileti;
$metafon=metaphone($iletisi);
$kayitbulundu=0;

mysql_select_db($database_akillim, $akillim);
$query_kayit = "SELECT * FROM akilli WHERE metafon LIKE '%$metafon%' ORDER BY rand() LIMIT 1";
$kayit = mysql_query($query_kayit, $akillim) or die;
$row_kayit = mysql_fetch_assoc($kayit);
$totalRows_kayit = mysql_num_rows($kayit);
if ($totalRows_kayit>=1) {
	$kayitbulundu=1;

	$bunugonder=$row_kayit['karsilik'];
	$bukac=substr_count($bunugonder,'|');
	$rastgele=rand(0,$bukac);
	$bunugonder=explode('|',$bunugonder);
	$bunugonder=$bunugonder[$rastgele];
	$kayitbulundu=1;
$mesajj= '<br /><strong>Beybut Robot</strong>: '.$bunugonder;
	


}

if ($kayitbulundu<=0) {

$say=strlen($metafon);

$son=ceil(($say*50)/100);	

if ($say<=3) {
$son=2;	
} 



mysql_select_db($database_akillim, $akillim);
$query_kayit = "SELECT * FROM akilli WHERE levenshtein('$metafon', metafon) BETWEEN 0 AND '$son' ORDER BY rand() LIMIT 1";
$kayit = mysql_query($query_kayit, $akillim) or die;
$row_kayit = mysql_fetch_assoc($kayit);
$totalRows_kayit = mysql_num_rows($kayit);
if ($totalRows_kayit>=1) {

	$bunugonder=$row_kayit['karsilik'];
	$bukac=substr_count($bunugonder,'|');
	$rastgele=rand(0,$bukac);
	$bunugonder=explode('|',$bunugonder);
	$bunugonder=$bunugonder[$rastgele];
	
$mesajj= '<br /><strong>Beybut Robot</strong>: '.$bunugonder;
	


}

}

mysql_free_result($kayit);
//Geliştirilecek yapay zeka bölümü.

Mysql bağlantısını yaptıktan sonra gelen iletiyi metaphone olarak kodluyoruz. Böylece veritabanında kayıtlı metafon alanımızda like ve levenshtein kullanarak eşleştirme yapabileceğiz.

SELECT * FROM akilli WHERE levenshtein('$metafon', metafon) BETWEEN 0 AND '$son' ORDER BY rand() LIMIT 1

Sorgusu aynı ve metaphone harf sayısının %50’sini alıp; benzeyen kayıtlardan rastgele birini getirmesini söylüyor. Bu oranı artırırsanız daha fazla kayıt; azaltırsanız daha az kayıt elde edersiniz. Ortalama olması için %50 belirledim.

Geliştireceğimiz yapay zeka bölümü bu kadar. Temel olması açısından, yukarıdaki sohbetleri edebilen Beybut Robot’a şimdilik bu kadar akıl yeter.

Php’den Node.js’ye: Elephant.io

Elephant.io Php içerisinden Node.js sunucusuna mesaj göndermek için hazırlanmış bir kütüphane. Github’da Elephant.io’ya erişebilirsiniz. Src klasörünü projenizin olduğu dizine gönderip aşağıdaki kodları kullanarak Php’den Node.js’ye kolayca mesaj gönderebilirsiniz.

require_once("src/Client.php");
require_once("src/EngineInterface.php");
require_once("src/AbstractPayload.php");
require_once("src/Exception/SocketException.php");
require_once("src/Exception/MalformedUrlException.php");
require_once("src/Exception/ServerConnectionFailureException.php");
require_once("src/Exception/UnsupportedActionException.php");
require_once("src/Exception/UnsupportedTransportException.php");

require_once("src/Engine/AbstractSocketIO.php");
require_once("src/Engine/SocketIO/Session.php");
require_once("src/Engine/SocketIO/Version0X.php");
require_once("src/Engine/SocketIO/Version1X.php");
require_once("src/Payload/Decoder.php");
require_once("src/Payload/Encoder.php");

use ElephantIO\Client;
use ElephantIO\Engine\SocketIO\Version1X;


$istemci = new Client(new Version1X('http://beybut.com:1919'));



$istemci->initialize();
$istemci->emit('soyle', ['iletisi' => $mesajj,'kullanici' => '<br /><strong>Siz</strong>: '.$ileti]);
$istemci->close();

 

Node.js sunucumuza pek bir iş bırakmadık. Mesajı alıp iletsin yeterli. Mysql’u Php içerisinde kullandığımız için Node.js sunucusuna iki satırlık kod kaldı.

var io = require('socket.io').listen(1919); 

console.log('Beybut Robot Sunucusu Baslatildi.\nTest Sunucusuna Hos Geldiniz. \nTest Sunucusu v.1.0.0\nProgramlayan: Baris YESILCIMEN\nMart 2016.');

io.sockets.on('connection', function(socket){

//Hoş geldin mesajı
socket.emit('sunucu-iletisi', {'ileti':'Hoş geldiniz.<br />Ben Beybut Robot. Akıllı bir arkadaşınız olma yolunda ilerliyorum.<br />Benimle aşağıdaki kutucuk aracılığıyla sohbet edebilirsiniz. :-)'}); 
//Hoş geldin mesajı.

socket.on('soyle', function(data){


socket.emit('sunucu-iletisi', {'ileti':data.kullanici}); 
socket.emit('sunucu-iletisi', {'ileti':data.iletisi}); 

});
});


Bu son kodlamayla birlikte yapay zeka projemizin temellerini atmış olduk. Artık Mysql’daki sözcük sayısını artırarak; levenshtein’ı geliştirerek… insan zekasına çok yakın bir robot üretmemiz içten bile değil. Temelini attığım bu projeyi dallandırıp budaklandırarak hayal gücümüzün sınırılarını zorlayabiliriz.

Sorularınızı yorum bölümünden iletebilirsiniz. Bu makale şimdilik yeterince uzun oldu. Bana eşlik ettiğiniz için teşekkür ederim. Görüşmek üzere…