PALM API ile Kod Oluşturma
Deeplearning.ai’ın “Pair Programing with a Large Language Model” kursunun Türkçe özeti.
PaLM API
PaLM API, geliştiricilerin Google’ın PaLM 2 büyük dil modeline (LLM) erişmesine olanak tanıyan bulut tabanlı bir hizmettir. PaLM, aşağıdakiler de dahil olmak üzere çeşitli görevler için kullanılabilecek güçlü bir yapay zeka modelidir:
- İçerik oluşturma: PaLM metin oluşturabilir, dilleri çevirebilir, farklı türde yaratıcı içerik yazabilir ve sorularınızı bilgilendirici bir şekilde yanıtlayabilir.
- Yerleştirmeler (Embeddings): PaLM, kelimeler, ifadeler ve cümleler için son teknoloji ürünü yerleştirmeler oluşturabilir. Bu yerleştirmeler daha sonra anlamsal arama, metin sınıflandırma ve kümeleme gibi NLP görevleri için kullanılabilir.
PaLM API’ın kullanımı kolaydır ve çeşitli programlama dillerinden erişilebilir. Aynı zamanda yüksek düzeyde ölçeklenebilir olduğundan, büyük hacimli verileri işleyen uygulamalar oluşturmak için kullanabilirsiniz.
PaLM API’nin nasıl kullanılabileceğine dair bazı örnekler:
- İçerik oluşturma: Bir haber kuruluşu, okuyucuları için kişiselleştirilmiş haber makaleleri oluşturmak amacıyla PaLM API’ı kullanabilir.
- Çeviri: Bir çeviri hizmeti, daha doğru ve incelikli çeviriler sağlamak için PaLM API’ı kullanabilir.
- Yaratıcı yazarlık: Bir yazar, yeni hikayeler için beyin fırtınası yapmak veya daha ilgi çekici içerik yazmalarına yardımcı olmak için PaLM API’ı kullanabilir.
- Müşteri hizmetleri: Bir şirket, müşteri sorularını yanıtlayabilecek ve destek sağlayabilecek bir sohbet robotu oluşturmak için PaLM API’ı kullanabilir.
- Anlamsal arama (semantic search): Bir arama motoru, arama sorgularının anlamını daha iyi anlamak ve daha alakalı sonuçlar sağlamak için PaLM API’ını kullanabilir.
Palm API web sitesi:
Not: Modelin büyüklüğü ile modelin ismi olan hayvanın gerçek hayattaki büyüklüğü doğrusal orantılıdır.
1. Giriş
Başlarken bir API anahtarına, üretken YZ kütüphanelerine, temel Python kodlama yeteneklerine ihtiyaç duyacaksınız.
Palm API konfigürasyonları:
!pip install -q google.generativeai
import os
import google.generativeai as palm
from google.api_core import client_options as client_options_lib
palm.configure(
api_key=get_api_key(),
transport="rest",
client_options=client_options_lib.ClientOptions(
api_endpoint=os.getenv("GOOGLE_API_BASE"),
)
)
Yetkimiz olan tüm PALM modellerini şu şekilde görebiliriz:
for m in palm.list_models():
print(f"name: {m.name}")
print(f"description: {m.description}")
print(f"generation methods:{m.supported_generation_methods}\n")
print(m) # Model hakkındaki tüm detayları verir
İsteğinizin başarısız olması durumunda API çağrısını yeniden denemenize yardımcı olması için @retry dekoratörünü kullanabilirsiniz.
from google.api_core import retry
@retry.Retry()
def generate_text(prompt,
model=model_bison,
temperature=0.0):
return palm.generate_text(prompt=prompt,
model=model,
temperature=temperature)
LLM’e nasıl kod yazılacağını soralım
İpucu: “Show me” ile başlayan istemler, “write code to” ile başlayan istemlerine kıyasla PaLM LLM’i daha fazla ayrıntı ve açıklama vermeye teşvik eder.
prompt = "write code to iterate across a list in Python" # Direkt cevap verir
completion = generate_text(prompt)
print(completion.result)
prompt = "Show me how to iterate across a list in Python." # Ayrıntılı açıklar
completion = generate_text(prompt)
print(completion.result)
Giriş bölümü kodları:
2. Katar Şablonu (String Template) Oluşturmak
Bilgi istemi şablonu
- Hazırlama (priming): LLM’i yapmasını isteyeceğiniz görev türüne hazırlamak.
- Soru (question): Özel görevi belirtilir.
- Dekoratör (decorator): Çıktının nasıl sağlanacağı veya formatlanacağı bilgisi belirtilir.
prompt_template = """
{priming}
{question}
{decorator}
Your solution:
"""
priming_text = "You are an expert at writing clear, concise, Python code."
question = "create a doubly linked list"
Kodlama yapılmayan diğer istem mühendisliği görevlerinde, modelden görevi “adım adım” tamamlamasını isteyerek “düşünce zinciri yönlendirmesini” kullanmak yaygındır.
Aşağıdaki kodda önce seçenek 1'i, ardından seçenek 2'yi deneyelim.
# option 1
decorator = "Work through it step by step, and show your work. One step per line."
# option 2
# decorator = "Insert comments for each line of code."
prompt = prompt_template.format(priming=priming_text,
question=question,
decorator=decorator)
Gerektiğinde hata ayıklayın(debug). Örneğin, random.randint() işlevini kullanmak için random kütüphanesini içe aktarmanız (import) gerekebilir.
Katar şablonu oluşturmak bölümü kodları:
3. Eşli Programlama (Pair Programming) Senaryoları
1. Senaryo: Mevcut kodu iyileştirelim
LLM’ler, kodunuzu söz konusu dil için önerilen şekilde yeniden yazmanıza yardımcı olabilir. LLM’lerden Python kodunuzu daha ‘Pythonik’ bir şekilde yeniden yazmasını isteyebilirsiniz.
prompt_template = """
I don't think this code is the best way to do it in Python, can you help me?
{question}
Please explain, in detail, what you did to improve it.
"""
question = """
def func_x(array)
for i in range(len(array)):
print(array[i])
"""
completion = generate_text(
prompt = prompt_template.format(question=question)
)
print(completion.result)
Kodumuzu yeniden yazmanın birden fazla yolunu isteyelim
prompt_template = """
I don't think this code is the best way to do it in Python, can you help me?
{question}
Please explore multiple ways of solving the problem, and explain each.
"""
completion = generate_text(
prompt = prompt_template.format(question=question)
)
print(completion.result)
Markdown’ı markdown hücresine yapıştıralım
Model, işaretlemede tablo gibi görünen bir çıktı verirse, çıktıyı görüntülemeyi kolaylaştırmak için işaretlemeyi kopyalayıp işaretleme hücresine yapıştırabilirsiniz.
Modelden en ‘Pythonic’ yöntemlerden birini önermesini isteyelim
prompt_template = """
I don't think this code is the best way to do it in Python, can you help me?
{question}
Please explore multiple ways of solving the problem,
and tell me which is the most Pythonic
"""
completion = generate_text(
prompt = prompt_template.format(question=question)
)
print(completion.result)
2. Senaryo : Kodu basitleştirelim
LLM’den bir kod incelemesi yapmasını isteyebilirsiniz.
Not: Yeni satır karakterlerinin eklenmesinin/kaldırılmasının LLM tamamlamasını etkileyebileceğini unutmayın.
İlk seçeneği denedikten sonra, bunu 2. seçeneğe benzeyecek şekilde değiştirebiliriz (bu işaretleme hücresinde) ve tamamlamayı nasıl değiştirdiğini görebilirsiniz.
# option 1
prompt_template = """
Can you please simplify this code for a linked list in Python?
{question}
Explain in detail what you did to modify it, and why.
"""
# option 2
prompt_template = """
Can you please simplify this code for a linked list in Python? \n
You are an expert in Pythonic code.
{question}
Please comment each line in detail, \n
and explain in detail what you did to modify it, and why.
"""
question = """
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.nextval = None
class SLinkedList:
def __init__(self):
self.headval = None
list1 = SLinkedList()
list1.headval = Node("Mon")
e2 = Node("Tue")
e3 = Node("Wed")
list1.headval.nextval = e2
e2.nextval = e3
"""
completion = generate_text(
prompt = prompt_template.format(question=question)
)
print(completion.result)
3. Senaryo: Test senaryolarını yazalım
LLM’nin yalnızca İngilizce test senaryolarını döndürmek yerine birim testleri yazmaya teşvik etmek için “kod halinde” çıktı vermesini istediğinizi belirtmeniz yardımcı olabilir.
prompt_template = """
Can you please create test cases in code for this Python code?
{question}
Explain in detail what these test cases are designed to achieve.
"""
question = """
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.nextval = None
class SLinkedList:
def __init__(self):
self.head = None
def create_linked_list(data):
head = Node(data[0])
for i in range(1, len(data)):
node = Node(data[i])
node.nextval = head
head = node
return head
list1 = create_linked_list(["Mon", "Tue", "Wed"])
"""
completion = generate_text(
prompt = prompt_template.format(question=question)
)
print(completion.result)
4. Senaryo: Kodu daha verimli hale getirelim
Verimsiz yöntemlerden (gerekmediğinde özyinelemeyi kullananlar gibi) kaçınarak çalışma süresini iyileştirelim.
prompt_template = """
Can you please make this code more efficient?
{question}
Explain in detail what you changed and why.
"""
question = """
# Returns index of x in arr if present, else -1
def binary_search(arr, low, high, x):
# Check base case
if high >= low:
mid = (high + low) // 2
if arr[mid] == x:
return mid
elif arr[mid] > x:
return binary_search(arr, low, mid - 1, x)
else:
return binary_search(arr, mid + 1, high, x)
else:
return -1
# Test array
arr = [ 2, 3, 4, 10, 40 ]
x = 10
# Function call
result = binary_search(arr, 0, len(arr)-1, x)
if result != -1:
print("Element is present at index", str(result))
else:
print("Element is not present in array")
"""
completion = generate_text(
prompt = prompt_template.format(question=question)
)
print(completion.result)
LLM tarafından oluşturulan kodu deneyelim
Not: Oluşturulan kodun gerçekte ne yaptığını kontrol etmeyi unutmayın. Örneğin, kodun geri kalanı teknik olarak bozuk olsa bile kod, önceden tanımlanmış bir işlevi (bisect gibi) çağırdığı için çalışıyor olabilir.
5. Senaryo: Kodunuzda hata ayıklayalım.
“question” değişkeniyle verdiğimiz kodda kasıtlı olarak bir hata bulunmaktadır. Bakalım LLM onu bulabilecek mi?
Hata print metodundadır. Düğümlerin (node) boş olabileceği bir durum vardır ve bunları yazdırmaya çalışmak “null” hatası verir.
prompt_template = """
Can you please help me to debug this code?
{question}
Explain in detail what you found and why it was a bug.
"""
question = """
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class doubly_linked_list:
def __init__(self):
self.head = None
# Adding data elements
def push(self, NewVal):
NewNode = Node(NewVal)
NewNode.next = self.head
if self.head is not None:
self.head.prev = NewNode
self.head = NewNode
# Print the Doubly Linked list in order
def listprint(self, node):
print(node.data),
last = node
node = node.next
dllist = doubly_linked_list()
dllist.push(12)
dllist.push(8)
dllist.push(62)
dllist.listprint(dllist.head)
"""
Bu durumda ders videosunda gördüğünüz örneği oluşturmak için varsayılan sıcaklık (temperature) değeri olan 0.7'yi kullandığımıza dikkat edin.
Sıcaklık 0'dan büyükse, LLM çıktısında daha fazla rastgeleliği teşvik edildiğinden, çıktının ne olduğunu görmek için bu kodu birkaç kez çalıştırmak isteyebilirsiniz.
completion = generate_text(
prompt = prompt_template.format(question=question),
temperature = 0.7
)
print(completion.result)
Not: Size içgörü sağlamak ve kör noktaları kontrol etmek için LLM’leri kullanabilirsiniz, ancak oluşturulan kodun yapmasını istediğiniz şeyi yapıp yapmadığını kontrol etmeyi unutmayın.
Eşli programlama senaryoları bölümü kodları:
4. Technical Debt (Teknik Borç)
LLM’den karmaşık bir kod tabanını (code base) açıklamasını istemek.
#@title Complex Code Block
# Note: Taken from https://github.com/lmoroney/odmlbook/blob/63c0825094b2f44efc5c4d3226425a51990e73d6/BookSource/Chapter08/ios/cats_vs_dogs/CatVsDogClassifierSample/ModelDataHandler/ModelDataHandler.swift
CODE_BLOCK = """
// Copyright 2019 The TensorFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import CoreImage
import TensorFlowLite
import UIKit
/// An inference from invoking the `Interpreter`.
struct Inference {
let confidence: Float
let label: String
}
/// Information about a model file or labels file.
typealias FileInfo = (name: String, extension: String)
/// Information about the MobileNet model.
enum MobileNet {
static let modelInfo: FileInfo = (name: "converted_model", extension: "tflite")
}
/// This class handles all data preprocessing and makes calls to run inference on a given frame
/// by invoking the `Interpreter`. It then formats the inferences obtained and returns the top N
/// results for a successful inference.
class ModelDataHandler {
// MARK: - Public Properties
/// The current thread count used by the TensorFlow Lite Interpreter.
let threadCount: Int
let resultCount = 1
// MARK: - Model Parameters
let batchSize = 1
let inputChannels = 3
let inputWidth = 224
let inputHeight = 224
// MARK: - Private Properties
/// List of labels from the given labels file.
private var labels: [String] = ["Cat", "Dog"]
/// TensorFlow Lite `Interpreter` object for performing inference on a given model.
private var interpreter: Interpreter
/// Information about the alpha component in RGBA data.
private let alphaComponent = (baseOffset: 4, moduloRemainder: 3)
// MARK: - Initialization
/// A failable initializer for `ModelDataHandler`. A new instance is created if the model and
/// labels files are successfully loaded from the app's main bundle. Default `threadCount` is 1.
init?(modelFileInfo: FileInfo, threadCount: Int = 1) {
let modelFilename = modelFileInfo.name
// Construct the path to the model file.
guard let modelPath = Bundle.main.path(
forResource: modelFilename,
ofType: modelFileInfo.extension
) else {
print("Failed to load the model file with name: \(modelFilename).")
return nil
}
// Specify the options for the `Interpreter`.
self.threadCount = threadCount
var options = InterpreterOptions()
options.threadCount = threadCount
do {
// Create the `Interpreter`.
interpreter = try Interpreter(modelPath: modelPath, options: options)
} catch let error {
print("Failed to create the interpreter with error: \(error.localizedDescription)")
return nil
}
}
// MARK: - Public Methods
/// Performs image preprocessing, invokes the `Interpreter`, and process the inference results.
func runModel(onFrame pixelBuffer: CVPixelBuffer) -> [Inference]? {
let sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer)
assert(sourcePixelFormat == kCVPixelFormatType_32ARGB ||
sourcePixelFormat == kCVPixelFormatType_32BGRA ||
sourcePixelFormat == kCVPixelFormatType_32RGBA)
let imageChannels = 4
assert(imageChannels >= inputChannels)
// Crops the image to the biggest square in the center and scales it down to model dimensions.
let scaledSize = CGSize(width: inputWidth, height: inputHeight)
guard let thumbnailPixelBuffer = pixelBuffer.centerThumbnail(ofSize: scaledSize) else {
return nil
}
let outputTensor: Tensor
do {
// Allocate memory for the model's input `Tensor`s.
try interpreter.allocateTensors()
// Remove the alpha component from the image buffer to get the RGB data.
guard let rgbData = rgbDataFromBuffer(
thumbnailPixelBuffer,
byteCount: batchSize * inputWidth * inputHeight * inputChannels
) else {
print("Failed to convert the image buffer to RGB data.")
return nil
}
// Copy the RGB data to the input `Tensor`.
try interpreter.copy(rgbData, toInputAt: 0)
// Run inference by invoking the `Interpreter`.
try interpreter.invoke()
// Get the output `Tensor` to process the inference results.
outputTensor = try interpreter.output(at: 0)
} catch let error {
print("Failed to invoke the interpreter with error: \(error.localizedDescription)")
return nil
}
let results = [Float32](unsafeData: outputTensor.data) ?? []
// Process the results.
let topNInferences = getTopN(results: results)
// Return the inference time and inference results.
return topNInferences
}
// MARK: - Private Methods
/// Returns the top N inference results sorted in descending order.
private func getTopN(results: [Float]) -> [Inference] {
// Create a zipped array of tuples [(labelIndex: Int, confidence: Float)].
let zippedResults = zip(labels.indices, results)
// Sort the zipped results by confidence value in descending order.
let sortedResults = zippedResults.sorted { $0.1 > $1.1 }.prefix(resultCount)
// Return the `Inference` results.
return sortedResults.map { result in Inference(confidence: result.1, label: labels[result.0]) }
}
/// Loads the labels from the labels file and stores them in the `labels` property.
private func loadLabels(fileInfo: FileInfo) {
let filename = fileInfo.name
let fileExtension = fileInfo.extension
guard let fileURL = Bundle.main.url(forResource: filename, withExtension: fileExtension) else {
fatalError("Labels file not found in bundle. Please add a labels file with name " +
"\(filename).\(fileExtension) and try again.")
}
do {
let contents = try String(contentsOf: fileURL, encoding: .utf8)
labels = contents.components(separatedBy: .newlines)
} catch {
fatalError("Labels file named \(filename).\(fileExtension) cannot be read. Please add a " +
"valid labels file and try again.")
}
}
/// Returns the RGB data representation of the given image buffer with the specified `byteCount`.
///
/// - Parameters
/// - buffer: The pixel buffer to convert to RGB data.
/// - byteCount: The expected byte count for the RGB data calculated using the values that the
/// model was trained on: `batchSize * imageWidth * imageHeight * componentsCount`.
/// - isModelQuantized: Whether the model is quantized (i.e. fixed point values rather than
/// floating point values).
/// - Returns: The RGB data representation of the image buffer or `nil` if the buffer could not be
/// converted.
private func rgbDataFromBuffer(
_ buffer: CVPixelBuffer,
byteCount: Int
) -> Data? {
CVPixelBufferLockBaseAddress(buffer, .readOnly)
defer { CVPixelBufferUnlockBaseAddress(buffer, .readOnly) }
guard let mutableRawPointer = CVPixelBufferGetBaseAddress(buffer) else {
return nil
}
let count = CVPixelBufferGetDataSize(buffer)
let bufferData = Data(bytesNoCopy: mutableRawPointer, count: count, deallocator: .none)
var rgbBytes = [Float](repeating: 0, count: byteCount)
var index = 0
for component in bufferData.enumerated() {
let offset = component.offset
let isAlphaComponent = (offset % alphaComponent.baseOffset) == alphaComponent.moduloRemainder
guard !isAlphaComponent else { continue }
rgbBytes[index] = Float(component.element) / 255.0
index += 1
}
return rgbBytes.withUnsafeBufferPointer(Data.init)
}
}
// MARK: - Extensions
extension Data {
/// Creates a new buffer by copying the buffer pointer of the given array.
///
/// - Warning: The given array's element type `T` must be trivial in that it can be copied bit
/// for bit with no indirection or reference-counting operations; otherwise, reinterpreting
/// data from the resulting buffer has undefined behavior.
/// - Parameter array: An array with elements of type `T`.
init<T>(copyingBufferOf array: [T]) {
self = array.withUnsafeBufferPointer(Data.init)
}
}
extension Array {
/// Creates a new array from the bytes of the given unsafe data.
///
/// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit
/// with no indirection or reference-counting operations; otherwise, copying the raw bytes in
/// the `unsafeData`'s buffer to a new array returns an unsafe copy.
/// - Note: Returns `nil` if `unsafeData.count` is not a multiple of
/// `MemoryLayout<Element>.stride`.
/// - Parameter unsafeData: The data containing the bytes to turn into an array.
init?(unsafeData: Data) {
guard unsafeData.count % MemoryLayout<Element>.stride == 0 else { return nil }
#if swift(>=5.0)
self = unsafeData.withUnsafeBytes { .init($0.bindMemory(to: Element.self)) }
#else
self = unsafeData.withUnsafeBytes {
.init(UnsafeBufferPointer<Element>(
start: $0,
count: unsafeData.count / MemoryLayout<Element>.stride
))
}
#endif // swift(>=5.0)
}
}
"""
prompt_template = """
Can you please explain how this code works?
{question}
Use a lot of detail and make it as clear as possible.
"""
completion = generate_text(
prompt = prompt_template.format(question=CODE_BLOCK)
)
print(completion.result)
Kendi kodumuz ile deneyelim. CODE_BLOCK değişkenine bazı kodlar ekleyelim.
CODE_BLOCK = """
# replace this with your own code
def foo(a):
b = a + 1
return 2*b
"""
prompt_template = """
Can you please explain how this code works?
{question}
Use a lot of detail and make it as clear as possible.
"""
completion = generate_text(
prompt = prompt_template.format(question=CODE_BLOCK)
)
print(completion.result)
LLM’den karmaşık bir kod tabanını dokümante etmesini isteyelim.
prompt_template = """
Please write technical documentation for this code and \n
make it easy for a non swift developer to understand:
{question}
Output the results in markdown
"""
completion = generate_text(
prompt = prompt_template.format(question=CODE_BLOCK)
)
print(completion.result)
Kendi kodumuzla deneyelim
İstemi Swift yerine Python’a atıfta bulunacak şekilde biraz değiştirdiğimize dikkat edin.
CODE_BLOCK = """
# replace this with your own code
def foo(a):
b = a + 1
return 2*b
"""
prompt_template = """
Please write technical documentation for this code and \n
make it easy for a non Python developer to understand:
{question}
Output the results in markdown
"""
completion = generate_text(
prompt = prompt_template.format(question=CODE_BLOCK)
)
print(completion.result)
Teknik borç bölümü kodları: