めも

主にUnity

PC用中古テレビの選び方

The Lab Page - RTINGS.com 液晶テレビをPCモニタとして使うスレ Part72

どのようなテレビを選ぶか?

パネルの種類

素数、フレームレート

  • 4K120p入力(価格コム,5ch,6.7万~,2021/03~) 4K120p表示ができるモニターは2015年頃から存在するものの、入力できる液晶はここ最近になってできた。5chでどの機種がよいか日々議論が交わされているが、人によって求める機能が異なるためか、これといった結論が出ていないようである。(ほしい。)
  • 8K(価格コム,11.7万~,2017/08~)
  • 4K(価格コム,3.7万~2015/04~)

大きさ

  • 43インチ未満の4kテレビはほとんど無いため、下限は43インチとする。
  • 65インチ以上は価格が急上昇する上、FHD32インチ以上相当とやや粗いため、上限は55インチとする。
  • 55インチが大きすぎるかは人による。(視力や机の広さ、体格などによる。)筆者はやめておこうと思う。

年式

「[年式] [サイズ] -ジャンク」で2023/03に検索したときの落札相場を下の表に示したが、いくつか注意点がある。

  • 中古テレビはあまりに画面割れによる故障品が多く、ノイズが多い。
  • 逆に、ジャンクと書いてあってもリモコンやB-CASカードが無いだけの場合がある。(特にB-CASカードはこの用途ではなくとも何も困らない。)
  • 「4K」とわざわざ商品説明に書かないものが多いため、検索ワードに「4K」を入れなかった。その結果、特にまだ2Kテレビが製造されていた2015年あたりでは、2Kテレビが混ざっている可能性があるため、要注意である。
年式 43 49
2015 落札価格 最安 1,000円 最高 39,800円 平均 14,261円 落札価格 最安 3,450円 最高 40,000円 平均 16,221円
2016 落札価格 最安 3,800円 最高 41,500円 平均 19,380円 落札価格 最安 2,100円 最高 45,000円 平均 22,240円
2017 落札価格 最安 1円 最高 55,000円 平均 22,018円 落札価格 最安 1,700円 最高 60,000円 平均 24,635円
2018 落札価格 最安 1,000円 最高 57,800円 平均 24,895円 落札価格 最安 540円 最高 72,000円 平均 31,757円
2019 落札価格 最安 1,000円 最高 77,000円 平均 33,672円 落札価格 最安 10,638円 最高 99,501円 平均 44,848円
2020 落札価格 最安 1,800円 最高 87,000円 平均 33,217円 落札価格 最安 15,500円 最高 105,000円 平均 47,399円
2021 落札価格 最安 5,250円 最高 150,000円 平均 44,671円 落札価格 最安 30,306円 最高 90,200円 平均 61,499円
2022 落札価格 最安 2,000円 最高 101,200円 平均 47,856円 落札価格 最安 14,520円 最高 125,000円 平均 82,890円

送料

らくらく家財宅急便がよく使われる。大型家具家電の輸送 らくらく料金検索 | ヤマトホームコンビニエンス

サイズ 家財便ランク 料金
43 B 4400-
49 B-C1 4400,7535-
55 C 7535-
65 C-D 7535,10945-

というわけで、再現性がある落札価格としては、1.6万が下限であるように思う。(逆に新品が3.7万で買えることを考えれば、この値段で買えなければあまり中古で買う意味が無い気がする。)

応答速度

ゲームに適した液晶テレビ126台目©2ch.net

ということで2015-16年の4Kテレビを調べることになったが、かなり情報がまとまっていない。

  • SONYは30ms程度。SONYは機種が同じでもサイズによってパネルがVAであったりIPSであったりすることがあるため注意。 2
  • LGは2015年は50ms程度、2016年では25ms程度。[^sonylg]
  • 東芝REGZA G20Xは11.3msと優秀。3

Panasonic4, Samsung, TCL, Hisense, Sharp, VizioはVAのため除外。

この他にもFUNAI等メーカーは存在するが、つかれたので割愛。 60p入力しかできない倍速液晶は、性質上表示開始時間が0.5フレーム分(8ms)遅延する。

Pythonで非同期の雰囲気(修正)

概要

重い処理を同時実行したいときに、非同期を使う。Pythonの非同期処理には2つのライブラリが存在し、以下のような簡単な方法でどちらを使うべきか知ることができる。

  • asyncio

    重い処理がasyncioに対応しているとき。(I/O待ちであるとき=await asyncio.sleep(n)がその関数の中で呼ばれているとき。)

  • concurrent.futuresProcessPoolExecutorおよび joblibParrarel

    重い処理がasyncioに対応していないとき。(CPUが動いているとき。)

どうでもいい注意

  • joblibで使われるlokyはこのconcurrent.futuresが元になっているが、ローカル関数が使えたり、(デッドロックせずに)入れ子された(nested)非同期処理が可能になるため、より推奨される。
  • ProcessPoolExecutorは内部でmultiprocessingモジュールを使っているが、このモジュールを直接使うのは特別な理由がない限り推奨しない。
  • これらはasyncio.loop.run_in_executorで併用できるが、あまり使われない。
  • わかりやすくするためにtasksresultsを分けて書いたが、joblibでは普通一行で書く。

asyncio

import asyncio

#目的の関数
async def function():
    pass

async def loop():
    tasks = [asyncio.create_task(function()) for i in range(10)]
    results = await asyncio.gather(*tasks)
    return results
    
def main():
    results = asyncio.run(loop)

joblib

import joblib

#目的の関数
def function():
    pass

def loop():
    tasks = [joblib.delayed(function)() for i in range(10)]
    results = joblib.Parallel(n_jobs=-1)(tasks)
    return results
    
def main():
    results = loop()

concurrent.futures

import concurrent.futures
# import loky

#目的の関数
def function():
    pass

def loop():
    with concurrent.futures.ProcessPoolExecutor() as executor:
    # with loky.get_reusable_executor() as executor:
        tasks = [executor.submit(function) for i in range(10)]
        results = [future.result() for future in concurrent.futures.wait(tasks).done]
    return results
    
def main():
    results = loop()

ProgressBarをつける

pip install tqdm

asyncio

import asyncio
# import tqdm
import tqdm.asyncio

#目的の関数
async def function():
    pass

async def loop():
    tasks = [asyncio.create_task(function()) for i in range(10)]
    # results = [await f for f in tqdm.tqdm(asyncio.as_completed(tasks), total=len(tasks))] # 古いやり方
    results = await tqdm.asyncio.gather(*tasks)
    return results
    
def main():
    results = asyncio.run(loop())

joblib

pip install tqdm_joblib
import joblib
from tqdm_joblib import tqdm_joblib

#目的の関数
def function():
    pass

def loop():
    with tqdm_joblib():
        tasks = [joblib.delayed(function)() for i in range(10)]
        results = joblib.Parallel(n_jobs=-1)(tasks)
    return results
    
def main():
    results = loop()

concurrent.futures

import concurrent.futures
import tqdm

#目的の関数
def function():
    pass

def loop():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        tasks = [executor.submit(function) for i in range(10)]
        results = [future.result() for future in tqdm.tqdm(concurrent.futures.as_completed(tasks), total=len(tasks))]
    return results
    
def main():
    results = loop()

音声合成と著作権

注意:筆者は全く法律に詳しくなく、またよく間違った内容の記事を放出します。

データセット

  • 音声の内容(読み上げた文章)には作家の著作権が認められる。 1
  • 音声には実演家の著作権はなく、演技に創作性がある場合に限り、著作隣接権が認められる。 [^1]
  • 音声に利用規約がある場合、機械学習にあたっては、著作権法第三十条の四は強行規定であるかどうかが問題になる。 強行規定ではなくこのような状況は信義則に違反しないとする説が有力であるため、利用規約は有効である可能性が高いが、2 そうでないとする意見も多数存在する。3 また、定型約款の表示方法によってはみなし合意が成立するかどうかが問題になる場合がある。 [^2] 4

合成音声

  • 合成音声には実演家の創作性は残らないため、実演家の著作(隣接)権は認められない。[^1]
  • 音声合成ソフトウェアを道具として用いたと認められる場合、音声合成ソフトウェアのユーザーに著作権が認められる場合がある。[^1]
  • 多くの音声合成ソフトウェアでは出力音声に対して企業の著作権のような権利を認めるように利用規約を作っている。 合成音声は通常著作物ではないため、著作権法の各条文が強行規定であるかどうかに関係なく、契約自由の原則よりこのような利用規約は通常有効である?[要出典,独自研究]

参考文献

我々が弁護士の見解を伺ったところ,まず生声については,用意された短い文章を淡々と読み上げた録音音声データに過ぎず,著作物とは言えないと考えられ,また,その読み上げの際に芸術的な性質を有する演技が行われているわけでもないため,著作隣接権の対象たる実演にも該当しないと考えられるとのことである.

その生声からコエ生成エンジンによりコエが生成されるが,コエは単なるその人の声の特徴を抽出したエッセンスデータであるため,著作物とは言えない.

合成音声は,作家の創作物であるテキストの複製物という扱いになり,作家の著作権が合成音声にも及ぶと考えられる.通常の収録音声の場合,声優などの読み上げた者(実演家)が芸術的な演技を伴って創作されたと見なされた場合には,実演家にも著作隣接権が認められることがあるが,合成音声の場合,声優は合成音声の生成において何も芸術的な演技を行わないため,著作隣接権も認められないと考えられる.尚,ユーザの読み調整により合成音声に創作的な表現が顕れるような場合には,合成音声は作家の創作物のユーザによる翻案物という扱いになり,権利関係は,作家が原著作物であるテキストの著作権を,ユーザが翻案物たる合成音声の著作権を有し,作家は合成音声について原著作者として権利を行使できる,ということになる.

声の権利化と流通を実現する音声合成サービス―一般人から有名人まで多種多様な声が使える新しいプラットフォーム―より引用

VPS, VDS, DS, VMの選び方

要約

  • まずは各プロバイダーが提供している無料クレジットを使い倒そう。
  • 既に無料クレジットを使い倒している場合やサーバーを頻繁に変えたくない場合は、ServerHunterでCPU型番をGeekbench v5のスコアに照らし合わせて性能を予想しながら、なるべくSetup Feeが少なく日割りで契約できる好みのサーバーを探し出し、地雷回避のためTrustPilotでプロバイダーの評判を一応確認してから、契約しよう。

サーバーの種類

  • Dedicated Server 一つのサーバーで一つのインスタンスを実行する。普通のパソコンと同じ。
  • Virtual Server 仮想化を用いて複数のインスタンスを一つのサーバーで実行する。Virtual Private Server(VPS)とも呼ばれる。特に物理CPUと同等の性能が保証されているものをVirtual Dedicated Server(VDS)と呼ぶ。(VDSもVPSの一種である。)

仮想化技術の種類

VPSでよく用いられるプラットフォーム仮想化ソフトウェアには、以下の2つがある。

AWS C5 インスタンス1, GCP2

  • OpenVZ

FastVM, Clovux3

その他の仮想化ソフトウェアの詳細についてはComparison of platform virtualization software - Wikipediaを参照。

仮想化ソフトウェアは各インスタンスに仮想コア(vCPU)を割り当てる。ところで、実はこの2つのソフトウェアはどちらもvCPU数を物理CPU数よりも大きくすることができる。vCPU数はしばしば性能の指標とされるが、実際の性能を保証するものでは全くないため、要注意。4 5 KVMではCPUが共有されないというのは誤情報である。ただし、OpenVZはより少ないリソースで性能を出すことができるため、利益を重視している業者によく使われがちであるという意見もある。6 7

Setup Fee と契約期間

サーバーの初回契約時にSetup Feeを要求する業者があるが、AWS EC2が秒単位で使える事を考えると、実際にセットアップ自体にお金がかかるとは思えない。むしろ、Setup Feeは性能の悪いVPSを提供して顧客が1ヶ月でやめることを事前に想定して、2ヶ月分とっておこう+既存顧客を取っておこうという意図があるように思われる。Setup Feeを要求しない業者はたくさんあるので、最初からサーバーを長く使おうとSetup Feeを飲み込むべきではない。むしろ、契約し始める最初こそ、いろいろなプロバイダーで失敗することを想定して、なるべく無料トライアルや1時間・1日単位で契約できるプロバイダーを探すべきである。1ヶ月契約、特にSetup Feeを要求する1ヶ月契約は地雷である。

有名なプロバイダーでの無料クレジットの一覧

Linodeなどの一部のプロバイダーは特定のリンクから契約しなければ無料クレジットをもらえない場合がある。このとき普通に登録して無料クレジットがもらえなかったり、アフィリンクを踏んで意図せず知らない人にお金が入ったりすることが無いように注意が必要である。また、OCI(コア数に制限), DigitalOcean(Sharedのみ)8, GCP9など無料クレジットでは/支払履歴がなければ性能の低いインスタンスしか使えないプロバイダーもある。

Service Credit ($) Expiration (Month)
Azure 200 1
GCP 300 3
OCI 300 1
IBM 200 1
DigitalOcean 100 2
Linode 100 2
Vultr 50 1
OVH 100 2
Tencent Cloud 50 1

推奨するプロバイダー

国内VPSは英語で物事を検索するという発想が無い人のためにあるように思われるため、除外。その他上の基準に従って適当に検索していくともう両手で数えるほどしか残らないが、ここに要件を足してServerHunterに載っていないAWSなど(意外と安い)も追加で検討していき、地雷回避をやれば、わりあいと決まるのではないだろうか。

最適な換気量

二酸化炭素濃度と人間のパフォーマンスの関係

二酸化炭素濃度と人間のパフォーマンスには確かに相関関係があるである。上の2つの図からは、建築基準法で指定されている1000ppmであっても認知機能に影響があることがわかる。この現象には、NLRP3とIL-1βなるものが関係していると一番下の図に書いてあるが、よくわからなかった。

二酸化炭素濃度わずかでも高いと認知機能に限らず様々な問題が起きると一番下の図の論文は主張している。

出典:Associations of Cognitive Function Scores with Carbon Dioxide, Ventilation, and Volatile Organic Compound Exposures in Office Workers: A Controlled Exposure Study of Green and Conventional Office Environments | Environmental Health Perspectives | Vol. 124, No. 6 https://ehp.niehs.nih.gov/doi/10.1289/ehp.1510037#pane-pcw-references

出典:Is CO2 an Indoor Pollutant? Direct Effects of Low-to-Moderate CO2 Concentrations on Human Decision-Making Performance https://www.semanticscholar.org/paper/Is-CO2-an-Indoor-Pollutant-Direct-Effects-of-CO2-on-Satish-Mendell/309e7ebea6e880cd2393bc38596933813022d335

出典:Direct human health risks of increased atmospheric carbon dioxide | Nature Sustainability https://www.nature.com/articles/s41893-019-0323-1

人間からの二酸化炭素発生量

エネルギー代謝率(RMR) 作業程度 二酸化炭素発生量[m^3/(h・人)]
0 安静時 0.0132
0~1 極軽作業 0.0132~0.0242
1~2 軽作業 0.0242~0.0352
2~4 中等作業 0.0352~0.0572
4~7 重作業 0.0572~0.0902

「5.1 完全混合濃度に基づく基本必要換気量」http://tkkankyo.eng.niigata-u.ac.jp/HASS/5_1.html より引用

非定常濃度方程式

室内ガス濃度$K$、導入外気ガス濃度(定数)$K_o$、単位時間あたりガス発生量(定数) $M $、単位時間あたり換気量(定数)$Q$、部屋の容積(定数)$V$に対し、 $$ \begin{equation} V\frac{dK}{dt}=Q(K_o-K)+M \end{equation} $$ 変形して $$ K'+\frac{Q}{V}K=\frac{QK_o+M}{V} $$ ところで $$ (e^{\frac{Q}{V}t}K)'=e^{\frac{Q}{V}t}(K'+\frac{Q}{V}t) $$ より、 $$ (e^{\frac{Q}{V}t}K)'=e^{\frac{Q}{V}t}(\frac{QK_o+M}{V}) $$ 積分して $$ \begin{cases} K=K_o+\frac{M}{Q}+Ce^{-\frac{Q}{V}t} && Q \neq 0 \\ K=\frac{M}{V}t+C && Q = 0 \end{cases} $$ 初期条件$K(0)=K_o$とすれば、 $$ \begin{cases} K=K_o+\frac{M}{Q}(1-e^{-\frac{Q}{V}t})<K_o+\frac{M}{Q} && Q \neq 0 \\ K=\frac{M}{V}t+K_o && Q = 0 \end{cases} $$ 許容室内ガス濃度$ K_m $を設定すると、必要換気量$ Q_m $は、 $$ Q_m=\frac{M}{K_m-K_o} $$

最適制御

春秋ならば窓を開け放して$ Q $を大きくしたいが、夏冬は空調機を使う。空調機が使う電力(と電気代)が換気量に比例すると仮定する。 これは、外温$ T_o $ と室温$ T_r $が一定であり、外気を空調機に通してから室内に入れることと同等である。すると、$ Q $の最小化が問題になるが、窓を開けたり閉じたりするよりも、一定量の換気を常にした方が、排気のCO2濃度の平均が高いため、$ Q $が低下し電気代が下がる。したがって、一定量の換気をし続けた方がいいということになる。

代入

$M=0.0132 $m3/h, $K_o=400 \cdot 10^{-6}, K_m= 1000 \cdot 10^{-6}$とする。$ M $には適宜人数を掛ける。

必要換気量

$$ Q_m=22\ \mathrm{[m^{3}/h]} $$

Bang-Bang制御する場合に窓を開ける頻度

$$ \frac{(K_m-K_o)V}{M}=\frac{V}{22}\ \mathrm{[m^{3}/h]} $$

1畳は1.62m2、部屋の高さは普通2.4-2.5mであるから、$n$畳の場合$\frac{1.62 \cdot 2.4}{22} n=0.286 n$時間ごとに換気する。

面積[畳] 時間[h]
4 1.14
6 1.72
8 2.29
10 2.86

参考文献:「必要換気量の求め方|三菱電機 空調・換気・衛生」https://www.mitsubishielectric.co.jp/ldg/ja/air/guide/support/knowledge/detail_01.html

この値は本当に正しいか?

筆者はNDIR式のCO2モニタを購入し検証したところ、換気のない6畳間洋室で420ppmから1020ppmに上昇するのに2時間半程度かかった。(部屋にこもり続けた。)これは理論値の1.45倍である。図のグラフはやや上に凸であるように見えるため、部屋が完全に密閉されているわけではないことが原因であろう。

ここ数日CO2モニタを眺めていた感想だが、筆者の6畳間洋室では以下のようであるに思えるが、数値は時により変動し、部屋に居続けるのも大変であり、夜間はPCを落としているのでログが取れずうまく検証できていない。さらに、都市部では排気ガスと交通量の変化の影響で、時間帯により外気のCO2濃度は25-50ppm異なる、ご飯を食べたあとは消化により代謝が上がりCO2濃度の増加が早まるなど、他の誤差要因も存在する。

窓の状態 Q
密閉 20
2つ5mm 30
2つ12mm 40

将来

All forcing agents CO2 equivalent concentration.svg EfbrazilCC 表示-継承 4.0, リンクによる

数十年後では大気中のCO2濃度は無視できないほど上昇する可能性があり、その場合上の計算を改める必要があるだろう。

プロット

二酸化炭素濃度の時間推移

換気後を初期条件とした場合における、換気量と二酸化炭素濃度の最大値の関係

# Licensed under CC0: https://creativecommons.org/publicdomain/zero/1.0/deed.en
from numpy.typing import ArrayLike
import numpy as np
import matplotlib.pyplot as plt
import matplotx.styles
from pandas import Series, Timedelta, TimedeltaIndex
import pandas as pd

plt.style.use(matplotx.styles.dracula)

def calc_k(k_o: float, m: float, q: float, v: float, t: ArrayLike) -> ArrayLike:
    """Calculate the concentration of a fluid in a room.

    Parameters
    ----------
    k_o : float
        Concentration of the fluid in the outside air.
    m : float
        Generation rate of the fluid.
    q : float
        Ventilation rate of the room.
    v : float
        Volume of the room.
    t : ArrayLike
        Time.

    Returns
    -------
    ArrayLike
        Concentration of the fluid in the room at time t.
    """
    if q == 0:
        return k_o + m / v * t
    elif q < 0:
        raise ValueError("q must be positive.")
    return k_o + m / q * ( 1 - np.exp(-q / v * t) )

def calc_k_fin(k_o: float, m: float, q: float) -> float:
    """Calculate the concentration of a fluid in a room.

    Parameters
    ----------
    k_o : float
        Concentration of the fluid in the outside air.
    m : float
        Generation rate of the fluid.
    q : float
        Ventilation rate of the room.

    Returns
    -------
    float
        Concentration of the fluid in the room at time t=np.inf.
    """
    if q == 0:
        return np.inf
    elif q < 0:
        raise ValueError("q must be positive.")
    return k_o + m / q

k_o = 420e-6
m = 0.0132 # per hour
ns = [4, 6, 8, 10]

# plot change in concentration over time
t = np.linspace(0, 3, 480)
qs = [0] + (22 * (2. ** np.arange(-3, 3))).tolist()
fig, axes = plt.subplots(len(ns), 1, figsize=(10, 16))
for n, ax in zip(ns, axes):
    ax.set_title(f"{n} tatami mats")
    ax.set_ylabel("CO2 Concentration (ppm)")
    ax.set_xlabel("Time (h)")
    v = n * 1.62 * 2.44
    for q in qs:
        k = calc_k(k_o, m, q, v, t)
        k = Series(k, t) * 1e6
        k.plot(label=f"q={q}", legend=True, ax=ax, grid=True)
fig.tight_layout()

# plot final concentration vs ventilation rate
fig, axes = plt.subplots(len(ns), 1, figsize=(10, 16))
qs = 22 * (2. ** np.linspace(-3, 3, 100))
for n, ax in zip(ns, axes):
    ax.set_title(f"{n} tatami mats")
    ax.set_ylabel("Final CO2 Concentration (ppm)")
    ax.set_xlabel("Ventilation Rate (m^3/h)")
    ax.grid(visible=True, which="major", axis="both")
    ax.grid(visible=True, which="minor", axis="both", linestyle="--")
    Series([calc_k_fin(k_o, m, q) * 1e6 for q in qs], qs).plot(ax=ax, grid=True, logx=True, logy=True)
fig.tight_layout()

Testing async code in python

非同期でpytestを使う場合、pytest-asyncioを使うことになるが、やや使いにくいのでunittestのIsolatedAsyncioTestCaseを使った。

実装例

from asyncio import sleep
from unittest import IsolatedAsyncioTestCase
# test case を得るために非同期関数を呼ばなければいけないときが一番ややこしい。
async def add(a, b) -> int:# function to test
    await sleep(1)
    return a + b

async def get_test_case(x) -> tuple[int, int]:
    await sleep(1)
    return x, 2*x

@parameterized_class(('testcase_func', 'testcase_param', 'answer'), [
    (staticmethod(get_test_case), 2, 6),
    (staticmethod(get_test_case), 3, 9),
])
class TestClass1(IsolatedAsyncioTestCase):
    a: int
    b: int

    async def asyncSetup(self) -> None:
        self.a, self.b = await self.testcase_func(testcase_param)

    async def test_add() -> None:
        self.assertEqual(add(self.a, self.b), self.answer)

Python mlflow with SQLite導入手順(venv)@VSCode

見様見真似でやってみました。

1.Mlflowをインストール
pip install mlflow
2.VSCode taskを作成
		{
			"type": "shell",
			"label": "mytask.mlflowui",
			"command": "start http://127.0.0.1:5000\r\n& {myvenv}/Scripts/Activate.ps1 \r\nmlflow server --backend-store-uri sqlite:///mlruns.sqlite3 --default-artifact-root artifact",
			"presentation": {
				"echo": true,
				"reveal": "silent",
				"focus": false,
				"panel": "dedicated",
				"showReuseMessage": false,
				"clear": false
			},
			"problemMatcher": []
		},

{myvenv}を適宜編集する。
taskを実行する。まずブラウザが開かれ、自動的にmlruns.sqlite3が作成される。ブラウザを更新するとmlflowのuiが表示されるはずである。taskの実行にはTASK EXPLORERが便利。

3.{myvenv}/Lib/site-packages/usercustomize.pyを作成
import mlflow
from logging import getLogger, basicConfig
# Mlflow db path settings
basicConfig()
logger = getLogger('usercustomize.py')
DB_PATH = 'mlruns.sqlite3'
tracking_uri = f'sqlite:///{DB_PATH}'
mlflow.set_tracking_uri(tracking_uri)
logger.info('Mlflow tracking uri has been configured.')

以上。これで後はlog_なんとか等を普通にやり、表示するときには上のタスクを実行すれば良いはずである。