by Baris Ceviz


Posted on Friday 28 April 2017


Bot Framework ile Azure Cognitive Services Kullanımı

Geliştirdiğimiz chatbot ile Yapay Zeka servislerini kullanarak daha akıllı bir chatbot geliştirmeye bir adım atalım

Selamlar,

Bot Framework ve Chatbot yazı seriside bu sefer Azure Cognitive Services kullanarak daha akıllı bir chatbot geliştirmiş olacağız. Bu yazıda Cognitive Services ın Computer Vision API ını kullanarak chatbot umuza gönderdiğimiz resmi analiz edip, içerisinde kaç insan var, bu insan tanıdık mı, yaşı kaç ve içeriği nedir gibi analizler yapabileceğiz. Sözü kısa kesip işlemlerimize başlayalım.

Öncelikle Azure Portal üzerinden Cognitive Services API oluşturalım. Sol bölümdeki + New butonuna tıklayalım. Ardından Intelligence + Analytics sekmesinden Cognitive Services ı seçelim.

Bot Framework ile Azure Cognitive Services Kullanımı

Ardından Cognitive Services adını girelim. API Type bölümünü Computer Vision API olarak ayarlayalım. Başlangıç için Pricing Tier i PO (20 calls per minute, 5K Calls per month) olarak ayarlayalım. Eğer uygulamanız dakikada 20 istekden fazlasına gidiyor ise P1 i seçebilir ve daha üst fiyatlandırmalar için Azure Support bölümünden iletişime geçebilirsiniz. Resource group bölümüne var olan bir grup seçebilir veya projeniz için yeni bir resource group oluşturabilirsiniz. Bu adımlardan sonra asıl önemli noktaya geldik. Burası kimileri için üzücü bir durum. Azure Cognitive Services her Azure Subscription tiplerini desteklememektedir. Örneğin Visual Studio Enterprise için desteklemezken Visual Studio Enterprise: Bizspark subscription tiplerini desteklemektedir. Desteklemeyen subscription sahipleri için Azure Support üzerinden iletişime geçmeyi denemelerini tavsiye ederim. Bu yazııyı yazarken bu sorunu Azure Support a danıştım. Cevap gelirse güncelleyeceğim :)

Bot Framework ile Azure Cognitive Services Kullanımı

Güncellendi

Artık Visual Studio Enterprise subscription ı ile Cognitive Services API ı oluşturabiliyorsunuz. Azure Support ile iletişime geçtikten sonra ilginç bir şekilde değişiklikler oldu. Hatta tasarımı bile değişti :) Sanırım ya denk geldi ya da bir etkim oldu :) En azından böyle bir sorun kalmaması bizim için yeterli bir sonuç. Cognitive Service oluşturma ekranı aşağıdaki artık şekildeki gibi gözükmektedir.

Bot Framework ile Azure Cognitive Services

 Azure Cognitive Services oluşturma işlemimizi tamamladıktan sonra artık koda geçebiliriz. Bir önceki yazımda Bot Framework ile proje oluşturmaktan bahsetmiştim. Şimdi projemizde yazmamız gereken iki şey mevcut. Bunlardan birincisi kullanıcın gönderdiği resmi Azure Storage a upload etmek ve ardından Azure Cognitive Services Computer Vision API a gönderip gelen JSON sonucu kullanıcıya cevap olarak göndermektir. Azure Storage için gerekli upload kodlarını yazmak için hazırlıklara başlayalım.

Azure Storage oluşturmadıysanız, Azure Portal üzerinden bir tane Azure Storage oluşturalım.

Sol panelden + New butonuna tıklayalım. Storage sekmesine tıklayalım ve Storage account - blob, file, table, queue yu seçelim. Storage a isim verelim. Hızlı bir storage oluşturma işlemi için Resource Groups unuzu oluşturduğunuz AI Service i ile ayı gruba alabilirsiniz. Böylelikle projeniz için kullandığınız servisleri tek bir grup üzerinden yönetebilirsiniz. Location olarak en yakın bölge olan North Europe veya West Europe u seçebilirsiniz. Tüm adımları tamamladıktan sonra artık Storage ı oluşturalım.

Bot Framework ile Azure Cognitive Services Kullanımı

Bot Framework ile Azure Cognitive Services Kullanımı

Ardından oluşturduğumuz Azure Storage ın Connection String bilgilerini alalım ve Bot Framework uygulamamızın Web.config dosyasına yerleştirelim. Örnek bir Azure Storage Connection String aşağıdaki gibidir. <AccountName> ve <AccountKey> i değiştirerek de oluşturabilirsiniz.

DefaultEndpointsProtocol=https;AccountName=<AccountName>;AccountKey=<AccountKey>;EndpointSuffix=core.windows.net

Bot Framework ile Azure Cognitive Services Kullanımı

Artık Azure Storage için Helper yazmaya başlayabiliriz. Nuget Package Manager Console üzerinden Azure Storage kütüphanesini kuralım. Package Manager Console u açmak için Visual Studio üzerinden Tools > Nuget Package Manager > Package Manager Console a tıklayınız. Alt bölümde Windows Powershell açılacaktır. Aşağıdaki kodu Nuget Package Manager Console üzerinden çalıştıralım.

Install-Package WindowsAzure.Storage

Kurulum tamamlandıktan sonra Solution Exploer üzerinden projemizin içerisine Helpers klasörü açalım ve içerisine StorageHelper class ı oluşturalım.

Bot Framework ile Azure Cognitive Services Kullanımı

StorageHelper class ımızı Signleton pattern ine göre yazacağız. Bunun için bir static olarak erişilebilecek bir Instance property si oluşturuyoruz ve içerisine Storage Connection string ine erişebileceği bir property ve Upload methodumuzu yazıyoruz. Aynı zamanda namespace adında .Helpers ı silelim. Böylelikle projenin her bölümünde referans olarak eklemeden direk erişebilirsiniz.

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace CognitiveServicesBot
{
    public class StorageHelper
    {
        #region Singleton
        private static StorageHelper _instance;
        public static StorageHelper Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new StorageHelper();
                return _instance;
            }
        }
        #endregion

        public string ConnectionString
        {
            get
            {
                return ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString;
            }
        }


        CloudStorageAccount storageAccount;
        CloudBlobClient blobClient;
        CloudBlobContainer container;

        public StorageHelper()
        {
            storageAccount = CloudStorageAccount.Parse(ConnectionString);
            blobClient = storageAccount.CreateCloudBlobClient();
            container = blobClient.GetContainerReference("chatbotimages");
        }

        public async Task<string> UploadFile(Stream stream, string friendlyUrl,string extension)
        {
            if (await container.CreateIfNotExistsAsync())
                await container.SetPermissionsAsync(
                        new BlobContainerPermissions
                        {
                            PublicAccess = BlobContainerPublicAccessType.Blob
                        });
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(friendlyUrl + "-" + Guid.NewGuid().ToString().Split('-')[0] + extension);

            try
            {
                await blockBlob.UploadFromStreamAsync(stream);
                return blockBlob.Uri.ToString();
            }
            catch
            {
                return "";
            }
        }
    }
}

Bu işlemimizi tamamladıktan sonra Azure Cognitive Services için gerekli kodlarımızı yazmaya başlayabiliriz. Azure Cognitive Services için Azure Portal üzerinden API Key i almamız gerekmektedir. Computer Vision API ına istek atarken bu Key ile istek atacağız. Bunun için Azure Portal üzerinden oluşturduğunuz Cognitive Service e tıklayınız. Overview ekranında Endpoint inizi kopyalayın ve bir yere koyun. Ardından Keys bölümünden Key imizi de alalım.

Bot Framework ile Azure Cognitive Services

Azure Cognitive Services endpoint ve key ini de Web.config içerisine yerleştirelim.

 Bot Framework ile Azure Cognitive Services

 Azure Cognitive Services API isteğini attığımızda dönecek olan JSON ı bir model olarak alıp kullanmamız gerekecek ve bu yüzden projemize bir Models klasörü oluşturmamız ve içerisine kullandığımız Model class ları oluşturmamız gerekiyor.  Örnek bir JSON değeri olarak aşağıdaki sonuç gelmektedir.

{
  "categories": [
    {
      "name": "people_",
      "score": 0.85546875,
      "detail": {
        "celebrities": [
          {
            "name": "Inna",
            "faceRectangle": {
              "left": 789,
              "top": 375,
              "width": 364,
              "height": 364
            },
            "confidence": 0.9999237
          }
        ]
      }
    }
  ],
  "adult": {
    "isAdultContent": true,
    "isRacyContent": true,
    "adultScore": 0.8976278305053711,
    "racyScore": 0.963962197303772
  },
  "tags": [
    {
      "name": "tree",
      "confidence": 0.9973397850990295
    },
    {
      "name": "person",
      "confidence": 0.9917859435081482
    },
    {
      "name": "outdoor",
      "confidence": 0.991138756275177
    },
    {
      "name": "ground",
      "confidence": 0.9685319066047668
    },
    {
      "name": "swimsuit",
      "confidence": 0.8870635032653809
    }
  ],
  "description": {
    "tags": [
      "person",
      "outdoor",
      "clothing",
      "sitting",
      "swimsuit",
      "woman",
      "bench",
      "top",
      "man",
      "young",
      "laying",
      "water",
      "pool",
      "park",
      "talking",
      "board",
      "holding",
      "white",
      "phone",
      "people",
      "standing"
    ],
    "captions": [
      {
        "text": "Inna sitting on a bench",
        "confidence": 0.6065563327890057
      }
    ]
  },
  "requestId": "9532761a-a58c-4de3-bbd1-72655f732d1c",
  "metadata": {
    "width": 2560,
    "height": 2560,
    "format": "Jpeg"
  },
  "faces": [
    {
      "age": 29,
      "gender": "Female",
      "faceRectangle": {
        "left": 790,
        "top": 374,
        "width": 364,
        "height": 364
      }
    }
  ],
  "color": {
    "dominantColorForeground": "Green",
    "dominantColorBackground": "Green",
    "dominantColors": [
      "Green",
      "White"
    ],
    "accentColor": "03B7C8",
    "isBWImg": false
  },
  "imageType": {
    "clipArtType": 0,
    "lineDrawingType": 0
  }
}

Bu JSON sonucuna göre bir Model classlarımızı üreteceğiz. Bunun için Projemize Models klasörü oluşturalım. Ardından CognitiveServiceModel adında bir class oluşturalım. Bunun içerisinde oluşan class ı silelim. Yukarıdaki JSON ı kopyalayıp Visual Studio üzerinden Edit > Paste Special > Paste JSON As Class a tıklayalım ve otomatik olarak JSON ı C# Class ına çevirelim. RootObject yazan class ismi yerine CognitiveServiceModel ismini verelim. Burada isterseniz direk kullanabilirsiniz ama ben biraz daha temiz bir Model olması için birkaç değişiklik yaptım. Değişiklik yaptığım model i aşağıdan kopyalayabilirsiniz.

    public class CognitiveServiceModel
    {
        [JsonProperty("categories")]
        public List<Category> Categories { get; set; }
        [JsonProperty("adult")]
        public Adult Adult { get; set; }
        [JsonProperty("tags")]
        public List<Tag> Tags { get; set; }
        [JsonProperty("description")]
        public Description Description { get; set; }
        [JsonProperty("requestId")]
        public string RequestId { get; set; }
        [JsonProperty("metadata")]
        public Metadata Metadata { get; set; }
        [JsonProperty("faces")]
        public List<Face> Faces { get; set; }
        [JsonProperty("color")]
        public Color Color { get; set; }
        [JsonProperty("imageType")]
        public Imagetype ImageType { get; set; }
    }

    public class Adult
    {
        [JsonProperty("isAdultContent")]
        public bool IsAdultContent { get; set; }
        [JsonProperty("isRacyContent")]
        public bool IsRacyContent { get; set; }
        [JsonProperty("adultScore")]
        public float AdultScore { get; set; }
        [JsonProperty("racyScore")]
        public float RacyScore { get; set; }
    }

    public class Description
    {
        [JsonProperty("tags")]
        public List<string> Tags { get; set; }
        [JsonProperty("captions")]
        public List<Caption> Captions { get; set; }
    }

    public class Caption
    {
        [JsonProperty("text")]
        public string Text { get; set; }
        [JsonProperty("confidence")]
        public float Confidence { get; set; }
    }

    public class Metadata
    {
        [JsonProperty("width")]
        public int Width { get; set; }
        [JsonProperty("height")]
        public int Height { get; set; }
        [JsonProperty("format")]
        public string Format { get; set; }
    }

    public class Color
    {
        [JsonProperty("dominantColorForeground")]
        public string DominantColorForeground { get; set; }
        [JsonProperty("dominantColorBackground")]
        public string DominantColorBackground { get; set; }
        [JsonProperty("dominantColors")]
        public List<string> DominantColors { get; set; }
        [JsonProperty("accentColor")]
        public string AccentColor { get; set; }
        [JsonProperty("isBWImg")]
        public bool IsBWImg { get; set; }
    }

    public class Imagetype
    {
        [JsonProperty("clipArtType")]
        public int ClipArtType { get; set; }
        [JsonProperty("lineDrawingType")]
        public int LineDrawingType { get; set; }
    }

    public class Category
    {
        [JsonProperty("name")]
        public string Name { get; set; }
        [JsonProperty("score")]
        public float Score { get; set; }
        [JsonProperty("detail")]
        public Detail Detail { get; set; }
    }

    public class Detail
    {
        [JsonProperty("celebrities")]
        public List<Celebrity> Celebrities { get; set; }
    }

    public class Celebrity
    {
        [JsonProperty("name")]
        public string Name { get; set; }
        [JsonProperty("faceRectangle")]
        public Facerectangle FaceRectangle { get; set; }
        [JsonProperty("confidence")]
        public float Confidence { get; set; }
    }

    public class Facerectangle
    {
        [JsonProperty("left")]
        public int Left { get; set; }
        [JsonProperty("top")]
        public int Top { get; set; }
        [JsonProperty("width")]
        public int Width { get; set; }
        [JsonProperty("height")]
        public int Height { get; set; }
    }

    public class Tag
    {
        [JsonProperty("name")]
        public string Name { get; set; }
        [JsonProperty("confidence")]
        public float Confidence { get; set; }
    }

    public class Face
    {
        [JsonProperty("age")]
        public int Age { get; set; }
        [JsonProperty("gender")]
        public string Gender { get; set; }
        [JsonProperty("faceRectangle")]
        public Facerectangle FaceRectangle { get; set; }
    }

Artık Azure Cognitive Service için kodlarımızı yazabiliriz. Projemize Services klasörü oluşturalım ve içerisine CognitiveService class ımızı oluşturalım. Bu class ımızı da Singleton pattern i ile yazalım.

    public class CognitiveServices
    {
        #region Signleton

        private static CognitiveServices _instance;

        public static CognitiveServices Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new CognitiveServices();
                return _instance;
            }
        }

        #endregion

        public string APIEndpoint
        {
            get
            {
                return ConfigurationManager.AppSettings["AzureCognitiveServicesEndpoint"];
            }
        }

        public string APIKey
        {
            get
            {
                return ConfigurationManager.AppSettings["AzureCognitiveServicesKey"];
            }
        }

        public async Task<CognitiveServiceModel> DescribeImage(string imageUrl)
        {
            using (HttpClient client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", APIKey);
                var response = await client.PostAsJsonAsync($"{APIEndpoint}/analyze?visualFeatures=Description,Tags,Color,Adult,Faces,Categories,ImageType&details=Celebrities&language=en", new
                {
                    Url = imageUrl
                });
                return await response.Content?.ReadAsAsync<CognitiveServiceModel>();
            }

        }
    }

Evet Cognitive Services için gerekli kodlarımızı da tamamladık. Computer Vision API a atacağımız istek ile gelen sonucu kullanıcıya göstermemiz yeterlidir. Bunun için projemizde Dialogs içerisindeki RootDialog class ına girelim. Parametre olarak gelen Activity içerisinde eğer bir dosya eklenmiş ise ve bu dosya resim ise bu resmi yorumlayalım. Ardından da sonucunu kullanıcıya dönelim.  Bunun için iki tane yardımcı method yazalım. Bunlardan bir tanesi bize Content type ına göre dosya uzantısı verebilecek bir method. Bir diğeri ise gelen Image ı Stream olarak alabilecek bir method

        private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
        {
            var activity = await result as Activity;

            string resultMessage = "";

            if (activity.Attachments != null && activity.Attachments.Count == 1)
            {
                Attachment file = activity.Attachments[0];
                Stream imageStream = await GetAttachmentAsStream(file.ContentUrl);
                if (imageStream != null)
                {
                    string imageUrl = await StorageHelper.Instance.UploadFile(imageStream, file.Name, GetExtension(file.Name));
                    if (!string.IsNullOrEmpty(imageUrl))
                    {
                        var AIResult = await CognitiveServices.Instance.DescribeImage(imageUrl);
                        if (AIResult != null)
                        {
                            resultMessage = AIResult.Description.Captions.FirstOrDefault()?.Text;
                        }
                        else
                        {
                            resultMessage = "Image describing has failed";
                        }
                    }
                    else
                        resultMessage = "Image uploading has failed";
                }
                else
                    resultMessage = "Image getting has failed";
            }

            await context.PostAsync(resultMessage);

            context.Wait(MessageReceivedAsync);
        }

        private async Task<Stream> GetAttachmentAsStream(string url)
        {
            using (HttpClient client = new HttpClient())
            {
                var response = await client.GetAsync(url);
                if (!response.IsSuccessStatusCode) return null;
                return await response.Content?.ReadAsStreamAsync();
            }
        }

        private string GetExtension(string contentType)
        {
            Dictionary<string, string> contentTypes = new Dictionary<string, string>()
            {
                {"image/png",".png" },
                {"image/jpg",".jpg" },
                {"image/jpeg",".jpeg" }
            };
            if (contentTypes.ContainsKey(contentType))
                return contentTypes[contentType];
            return null;
        }

Evet artık test zamanı. Şimdi Bot Framework Emulator ünü başlatıp servisimizi test edelim. Bununla ilgili bilgiler bir önceki yazımda mevcuttur. Aşağıda nasıl çalıştığını da görüyoruz :)

Bot Framework ile Azure Cognitive Services

Evet bu yazınında sonuna gelmiş bulunmaktayız. Gördüğünüz gibi bazen AI Servisleri de hata yapabiliyor. Şahsen bu fotoğrafa bende böyle bir cevap verirdim :) Bu yazımda geliştirdiğim chatbot un kodlarını aşağıdaki github adresinden bulabilirsiniz.

https://github.com/peacecwz/CognitiveServicesBot

Github üzerinden takip etmeyi unutmayalım arkadaşlar :)