超コンパクトでも高性能 Phi-4を試す
Microsoftが超コンパクト(と言っても14B)で70Bモデル超えの高性能を売りにしたLLM、Phi-4を発表した。Ollamaにも入っていたので早速試してみた。
元々Phiシリーズは、1.5Bとかの極端に小さいモデルでもそこそこ使える、会話できるというところを目指して作られていた。そこからすると14Bはまあまあ巨大なのだが、もしかするとCopilot+PCで動作させることを意識してるのかもしれない。
このサイズなので従来のPhiシリーズより賢いのは当たり前なのだが、一応試してみる。
% ollama run phi4
>>> quaternionについて説明して
クォータニオン(Quaternion)は、ベクトル解析や回転表現で広く使用される数学的構造です。通常の3次元空間における回転を扱う際に特に便利
とされています。以下は、クォータニオンの基本的な説明です。
### 概要
クォータニオンは4次元の数であり、1つの実部(スカラー成分)と3つの虚部(ベクトル成分)から構成されます。一般的にクォータニオン $${ q
}$$ は以下のように表されます:
$${ q = w + xi + yj + zk }$$
ここで、$${ w, x, y, z }$$ は実数であり、$${ i, j, k }$$ は虚数単位です。これらの単位は以下のような関係を満たします:
- $${ i^2 = j^2 = k^2 = ijk = -1 }$$
- $${ ij = k, \quad ji = -k }$$
- $${ jk = i, \quad kj = -i }$$
- $${ ki = j, \quad ik = -j }$$
### 特徴
1. **回転の表現**: クォータニオンは3次元空間における回転を直感的かつ効率的に表すことができます。特定の角度と回転軸を用いて、任意の点
やベクトルの回転を計算する際に有用です。
2. **スキュー関数(シフト)**: クォータニオンは3D空間での移動と回転(ローテーション・トランスレーション)を一貫して扱うことができま
す。特に、キャメラやロボットの姿勢制御などに利用されます。
3. **ソフトウェアおよびゲーム開発**: 三次元環境での回転を扱う際、クォータニオンは歪みが少なく、数学的に安定しているため、コンピュー
タグラフィックスやロボティクス、VR/ARアプリケーションで広く使用されます。
4. **環境の連鎖的な結合**: クォータニオンは他のクォータニオンとの積(乗算)を通じて、複数の回転を効率よく結合することができます。
### 計算
- **加法**: クォータニオン同士は成分ごとに足し算されます。
- **乗法**: 2つのクォータニオン $${ q_1 = a_1 + b_1i + c_1j + d_1k }$$ と $${ q_2 = a_2 + b_2i + c_2j + d_2k }$$ の積は以下のように計算
されます。
$${
q_1 q_2 = (a_1a_2 - b_1b_2 - c_1c_2 - d_1d_2) + (a_1b_2 + b_1a_2 + c_1d_2 - d_1c_2)i \\
+ (a_1c_2 - b_1d_2 + c_1a_2 + d_1b_2)j + (a_1d_2 + b_1c_2 - c_1b_2 + d_1a_2)k
}$$
- **複素共役**: クォータニオン $${ q = w + xi + yj + zk }$$ の共役は $${ q^* = w - xi - yj - zk }$$ です。
- **逆数**: もしクォータニオン $${ q }$$ の大きさ(ノルム)が0でない場合、その逆数は以下のように計算されます。
$${
q^{-1} = \frac{q^*}{|q|^2}
}$$
ここで、$${ |q| = \sqrt{w^2 + x^2 + y^2 + z^2} }$$ です。
### 応用
- **コンピュータグラフィックス**: モデルの回転やカメラワーク。
- **ロボティクス**: ロボットアームの制御。
- **航空宇宙工学**: 衛星の姿勢制御。
以上がクォータニオンに関する基本的な説明です。それらは数学的に強力で、3Dコンピューティング分野で特に価値を持っています。
なかなか良い。というかたいていの人間よりは賢い。
slerpについても説明してもらおう
>>> slerpについて説明して
SLERP(Spherical Linear Interpolation)は、クォータニオンの間をスムーズに補間するための手法です。特に3Dグラフィックスやロボティクス
で回転を自然に移行させる際に使用されます。SLERPは、球面上での直線的な補間として理解することができます。
### SLERPの特徴
1. **一定速度**: SLERPでは、回転角の変化が時間に対して均等です。これは、時間経過に応じて同じ量だけ回転するという意味であり、直感的な
動きを実現します。
2. **球面上の補間**: SLERPはクォータニオンが表す4次元ユークリッド空間において行われます。このため、補間結果も回転操作として正しいこ
とを保証します。
3. **正規化**: SLERPで得られるクォータニオンは常に正規化されています(ノルムが1)。これは、補間結果が有効な回転表現として保持される
ことを意味します。
### SLERPの計算
2つのクォータニオン $${ q_0 }$$ と $${ q_1 }$$ の間でパラメータ $${ t \in [0, 1] }$$ を用いて補間する場合、SLERPは以下のように計算されます
。
1. **内積を求める**:
$${
\text{dot} = q_0 \cdot q_1
}$$
2. **必要に応じて共役を取り、補間方向を調整する**:
- もし $${\text{dot} < 0}$$ の場合は、$${ q_1 = -q_1 }$$ を設定します。これは、二つのクォータニオンが反対側にある場合に補間をスムース
にするためです。
3. **角度と距離を計算**:
- 角度 $${\theta}$$ は以下で求まります。
$${
\theta = \arccos(\text{dot})
}$$
4. **SLERPの補間式**:
- $${ t = 0 }$$ のとき、結果は $${ q_0}$$、$${ t = 1 }$$ のときには $${ q_1 }$$ です。
- 中間点を計算する際の式は以下です:
$${
q(t) = \frac{\sin((1-t)\theta)}{\sin(\theta)} q_0 + \frac{\sin(t\theta)}{\sin(\theta)} q_1
}$$
### 使用例
SLERPは、アニメーションのフレーム間でキャラクターのモデルを滑らかに回転させたり、ロボットがゆっくりと目標姿勢に移行する際に用いられ
ます。例えば、3Dビデオゲームでキャラクターのカメラをスムーズに向きを変えたり、仮想現実(VR)アプリケーションでヘッドトラッキングを行
う際などに利用されます。
SLERPは非常に有効ですが、計算コストも少し高いため、必要な場面でのみ使用すると良いでしょう。また、クォータニオンの内積が-1に近い場合
(ほぼ180度回転)は別の補間手法を考慮することもあります。
ここで試したのはQ4_K_Mなので、もっと良い答えが欲しければQ8とかFP16とかも選択肢に入る。Q4_K_Mはわずか9GBしかない。M2以降のMacなら余裕だし、このレベルのLLMを持ち歩けるのは単純に嬉しい。
プログラムも書けるかやってみる。
>>> テトリス作って
テトリスをプログラムで作成するには、ゲームの基本的なルールやコンポーネントを理解し、それらを実装する必要があります。以下はシンプルな
Python版テトリスの概要と実装例です。
### 必要なコンポーネント
1. **ゲームフィールド(ボード)**: 通常10x20の2D配列で表現されます。
2. **テトリミノブロック**: 7種類の形状からランダムに選ばれ、回転や移動が可能です。
3. **ゲームルール**:
- テトリミノをフィールドに落とし、全ての行を埋めると消去します。
- 消去した後で新しいブロックが生成されます。
- 特定のレベルに達するごとに落下速度が上昇します。
### 実装例(Python)
以下は基本的なテトリスゲームを作成するための簡単なコードです。`curses`ライブラリを使用して、ターミナルでプレイ可能にします。
```python
import random
import time
import curses
# テトリミノの形状(0が空白)
TETROMINOES = [
[[1, 1, 1, 1]], # I
[[2, 2], [2, 2]], # O
[[3, 0, 0], [3, 3, 3]], # T
[[4, 4, 0], [0, 4, 4]], # S
[[0, 5, 5], [5, 5, 0]], # Z
[[6, 6, 6], [0, 6, 0]], # J
[[0, 7, 7], [7, 7, 0]] # L
]
def rotate_clockwise(shape):
return [ [ shape[y][x] for y in range(len(shape)) ]
for x in range(len(shape[0]) - 1, -1, -1) ]
def check_collision(board, piece, offset):
off_x, off_y = offset
for cy, row in enumerate(piece):
for cx, cell in enumerate(row):
try:
if cell and board[cy + off_y][cx + off_x]:
return True
except IndexError:
return True
return False
def join_matrices(mat1, mat2, mat2_off):
off_x, off_y = mat2_off
for cy, row in enumerate(mat2):
for cx, val in enumerate(row):
if val:
mat1[cy + off_y][cx + off_x] = val
return mat1
def clear_rows(board):
new_board = [row for row in board if any(cell == 0 for cell in row)]
cleared_rows = len(board) - len(new_board)
new_board = [[0 for _ in range(10)]] * cleared_rows + new_board
return new_board, cleared_rows
def main(stdscr):
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK)
curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK)
stdscr.nodelay(True)
curses.curs_set(False)
screen = curses.initscr()
h, w = 20, 10
board = [[0 for _ in range(w)] for _ in range(h)]
def print_board(board):
for y, row in enumerate(board):
for x, cell in enumerate(row):
stdscr.addstr(y, x*2, "██" if cell else " ")
stdscr.refresh()
falling_piece = random.choice(TETROMINOES)
piece_offset = [w // 2 - len(falling_piece[0]) // 2, 0]
while True:
time.sleep(0.5)
next_y = piece_offset[1] + 1
if not check_collision(board, falling_piece, (piece_offset[0], next_y)):
piece_offset = [piece_offset[0], next_y]
else:
board = join_matrices(board, falling_piece, piece_offset)
board, cleared_rows = clear_rows(board)
if any(cell != 0 for row in board[:4] for cell in row):
break
piece_offset = [w // 2 - len(falling_piece[0]) // 2, 0]
falling_piece = random.choice(TETROMINOES)
print_board(board)
curses.wrapper(main)
```
### 説明
- **TETROMINOES**: 各テトリミノの形状をリスト化。
- **rotate_clockwise**: テトリミノを時計回りに90度回転させる関数。
- **check_collision**: ボード上での衝突をチェックする関数。
- **join_matrices**: 新しいブロックが落下した位置に反映されるようにボードとブロックを結合する。
- **clear_rows**: 完全に埋まった行を消去し、得点を返す関数。
### 注意事項
このコードは基本的なテトリスのプレイ感覚を表現していますが、完全なゲームとしては以下を考慮する必要があります:
- **制御**: キー入力によるブロックの移動や回転。
- **スコアシステム**: 行を消去した際の得点計算とレベル管理。
- **ゲームオーバーの判定**。
このコードは`curses`ライブラリで動作し、ターミナル上で簡易的なグラフィックを表示します。さらに洗練されたインターフェースや機能が必要
な場合は、Pygameのようなゲーム開発用ライブラリも検討できます。
このプログラムを実行したところ、闇夜のカラスのようなアホみたいな実行結果になった。
Pygameで作り直させてみる。
>>> pygameでテトリスを作って
Pygameを使って簡単なテトリスクラシックゲームを作成するには、いくつかの主要なコンポーネントが必要です。ここでは基本的な構造と処理方法
を説明します。
### 必要な準備
まず、Pygameがインストールされていることを確認してください:
```bash
pip install pygame
```
### プログラムの基本構造
以下に簡単なテトリスゲームのサンプルコードを示します。
```python
import pygame
import random
import sys
# Pygame初期化
pygame.init()
# ウィンドウ設定
WINDOW_WIDTH = 300
WINDOW_HEIGHT = 600
BLOCK_SIZE = 30
GRID_WIDTH = WINDOW_WIDTH // BLOCK_SIZE
GRID_HEIGHT = WINDOW_HEIGHT // BLOCK_SIZE
# 色の設定
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# フォント設定
pygame.font.init()
font = pygame.font.Font(None, 36)
# ゲームクラス
class Tetris:
def __init__(self):
self.grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
self.game_over = False
self.score = 0
# テトリミノの形状定義(I, O, T, S, Z, J, L)
self.shapes = [
[[1, 1, 1, 1]], # I
[[1, 1], [1, 1]], # O
[[0, 1, 0], [1, 1, 1]], # T
[[0, 1, 1], [1, 1, 0]], # S
[[1, 1, 0], [0, 1, 1]], # Z
[[1, 0, 0], [1, 1, 1]], # J
[[0, 0, 1], [1, 1, 1]] # L
]
self.current_shape = self.new_piece()
self.next_shape = self.new_piece()
def new_piece(self):
shape = random.choice(self.shapes)
position = [0, GRID_WIDTH // 2 - len(shape[0]) // 2]
return {'shape': shape, 'position': position}
def draw_grid(self, screen):
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
rect = pygame.Rect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
color = WHITE if self.grid[y][x] else BLACK
pygame.draw.rect(screen, color, rect)
def draw_shape(self, screen, shape, position):
for y, row in enumerate(shape['shape']):
for x, cell in enumerate(row):
if cell:
rect = pygame.Rect((position[0]+x) * BLOCK_SIZE, (position[1]+y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
pygame.draw.rect(screen, WHITE, rect)
def rotate(self, shape):
# テトリミノの回転(90度)
return {'shape': [list(row) for row in zip(*shape['shape'][::-1])], 'position': shape['position']}
def move_down(self):
shape = self.current_shape
if not self.collision(shape, 0, 1):
shape['position'][1] += 1
else:
self.lock_piece()
self.clear_lines()
if not self.start_new_piece():
self.game_over = True
def start_new_piece(self):
self.current_shape = self.next_shape
self.next_shape = self.new_piece()
return not self.collision(self.current_shape, 0, 0)
def lock_piece(self):
shape = self.current_shape
for y, row in enumerate(shape['shape']):
for x, cell in enumerate(row):
if cell:
self.grid[shape['position'][1] + y][shape['position'][0] + x] = 1
def clear_lines(self):
lines_to_clear = [index for index, row in enumerate(self.grid) if all(row)]
for index in lines_to_clear:
del self.grid[index]
self.grid.insert(0, [0 for _ in range(GRID_WIDTH)])
self.score += 1
def collision(self, shape, dx=0, dy=0):
for y, row in enumerate(shape['shape']):
for x, cell in enumerate(row):
if cell:
nx = shape['position'][0] + x + dx
ny = shape['position'][1] + y + dy
if nx < 0 or nx >= GRID_WIDTH or ny >= GRID_HEIGHT or (ny >= 0 and self.grid[ny][nx]):
return True
return False
def draw_next_shape(self, screen):
next_rect = pygame.Rect(WINDOW_WIDTH - BLOCK_SIZE * len(self.next_shape['shape']), 0, BLOCK_SIZE *
len(self.next_shape['shape']), BLOCK_SIZE * len(self.next_shape['shape']))
for y, row in enumerate(self.next_shape['shape']):
for x, cell in enumerate(row):
if cell:
rect = pygame.Rect(next_rect.x + x * BLOCK_SIZE, next_rect.y + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
pygame.draw.rect(screen, WHITE, rect)
# メインループ
def main():
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("Tetris")
clock = pygame.time.Clock()
tetris = Tetris()
while not tetris.game_over:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and not tetris.collision(tetris.current_shape, -1, 0):
tetris.current_shape['position'][0] -= 1
elif event.key == pygame.K_RIGHT and not tetris.collision(tetris.current_shape, 1, 0):
tetris.current_shape['position'][0] += 1
elif event.key == pygame.K_DOWN:
if not tetris.collision(tetris.current_shape, 0, 1):
tetris.current_shape['position'][1] += 1
else:
tetris.lock_piece()
tetris.clear_lines()
if not tetris.start_new_piece():
tetris.game_over = True
elif event.key == pygame.K_UP:
rotated = tetris.rotate(tetris.current_shape)
if not tetris.collision(rotated):
tetris.current_shape = rotated
tetris.move_down()
screen.fill(BLACK)
tetris.draw_grid(screen)
tetris.draw_shape(screen, tetris.current_shape, tetris.current_shape['position'])
tetris.draw_next_shape(screen)
score_text = font.render(f'Score: {tetris.score}', True, WHITE)
screen.blit(score_text, (5, 5))
pygame.display.flip()
clock.tick(10) # ゲームの速度
if __name__ == "__main__":
main()
```
### 説明
1. **初期設定**:
- Pygameを初期化し、ウィンドウサイズとブロックサイズを設定します。
- ゲームのグリッドを作成し、各テトリミノの形状を定義します。
2. **ゲームクラス**:
- `Tetris`クラスは、現在と次のテトリミノを管理し、行削除や点数計算を行います。
- グリッドに対する衝突チェック、テトリミノの移動と回転、新しいテトリミノの生成などの機能を持ちます。
3. **メインループ**:
- イベントを処理し(キー入力による左右移動や回転)、ゲームロジックを更新します。
- スクリーンの描画とフレーム毎の更新を行います。
このコードは基本的なテトリスゲームです。拡張機能として、より高度なグラフィック表示や音声エフェクト、ハイスコア管理などが考えられます
。また、ゲームの難易度を調整するために、落下速度やゲームオーバー時の処理も追加できます。
確かにテトリスのようなゲームができた。
あまりにも落ちてくるスピードが速いのでスクショを撮ろうとすると縦に重なってしまったが、ちゃんと回転するし移動できる。
コンテキストサイズは16Kなので、そこまで複雑なものは作れないが、ちょっとしたプログラムを書かせるならたいていの人間よりマシだろう。
ただ、テトリスを書ける理由は、テトリスがアルゴリズムがよく知られたゲームだからであり、未知のゲームやよく知られていないゲームをいきなり作らせることは難しい。レイフォースのようなホーミングレーザーの出てくるゲームを作ってと言ったら案の定、全くデタラメなコードが出てきた。
ちょっとしたエラーメッセージの解釈やちょっとした翻訳にはもしかしたら使えるかもしれない。
さっきの文章を英訳させてみる。
>>> 以下のメッセージを英訳して テトリスを書ける理由は、テトリスがアルゴリズムがよく知られたゲームだからであり、未知のゲームやよく知
... られていないゲームをいきなり作らせることは難しい。レイフォースのようなホーミングレーザーの出てくるゲームを作ってと言ったら案の定、全
... くデタラメなコードが出てきた。
The reason why Tetris can be implemented is that it's a well-known game with familiar algorithms, making it challenging to start
creating an entirely new or unfamiliar game right away. If you were to ask someone to create a game like RayForce, which involves
homing lasers appearing, predictably, they would produce completely nonsensical code.
Here’s the translation:
"The reason Tetris can be written is that its algorithms are well-known. It's difficult to suddenly make an unfamiliar game from
scratch. If you asked for a game similar to RayForce with homing laser attacks, as expected, completely nonsensical code would
emerge."
あ、便利だね。このレベルの翻訳エンジンがローカルで動くようになったということだけでも喜ばしい。もちろん要約もできる。
昨日のnote(https://note.com/shi3zblog/n/n432905d15632)を要約させてみる。
要約エンジンの性能をなんとか上げようとしていた昔のことを考えると、まるで夢のようである。
DeepSeekV3のollama版もそろそろ出るらしいので期待したい。まあ動く環境は限られるが