Gorio Tech Blog search

Tree of Thoughts - Deliberate Problem Solving with Large Language Models (ToT) 요약 설명

|

이번 글에서는 Tree of Thoughts: Deliberate Problem Solving with Large Language Models 논문의 핵심 포인트만 간단히 정리한다.

  • 2023년 5월(Arxiv), NeurIPS 2023
  • Shunyu Yao et al.
  • Princeton University, Google DeepMind
  • 논문 링크
  • Github

요약

  • Exploration, strategic lookahead 등이 필요한 복잡한 추론 문제를 풀기 위해 tree of thoughts (ToT) 방법을 제안한다.
  • CoT를 generalize한 방법
  • 서로 다른 여러 reasoning paths를 탐색하고 다음 action을 결정할지를 self-evaluate한다.
  • Game of 24, Creative Writing, Mini Crosswords 등의 문제를 풀었고
  • GPT-4로 CoT를 할 때 4% 정도의 성능인 것을 74%까지 달성했다.
  • 4가지 부분으로 구성된다.
    • Thought decomposition: 적당한 크기의 thought로 분해하는 작업
    • Thought generator $G$: i.i.d thought를 CoT를 통해 생성하거나 (sample) 한 번에 여러 개의 thought를 생성함(propose)
    • State evaluator $V$: 현재 state에서 최적의 thought (state)를 평가하고 선택하는 작업
      • Value: 각 state를 독립적으로 평가함 (1-10 scale 또는 sure/likely/impossible 등). 논문에서는 task별로 다른 방식을 사용하였음.
      • Vote: 여러 state를 비교해서 best state를 선택함.
    • Search algorithm: tree에서 어떤 경로 탐색을 쓸 것인지에 관한 것인데 논문에서는 BFS (최대 $b=5$)와 DFS만 사용.
  • 각 task별 thought의 정의:
  • Game of 24:
  • Creative Writing:
  • Mini Crosswords:
Comment  Read more

Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena (LLM-as-a-Judge) 요약 설명

|

이번 글에서는 Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena 논문의 핵심 포인트만 간단히 정리한다.


요약

  • Chat assistant에 기반한 LLM 평가는 open-ended 특성상 평가 방법이 많이 없다.
  • 이 논문에서는 두 가지 benchmark를 제안한다.
    • MT-Bench: a multi-turn question set
    • Chatbot Arena: a crowdsourced battle platform
  • 이 논문에서 LLM-as-a-Judge는 크게 3가지 방법으로 평가할 수 있다.
      1. Pairwise comparison: 두 개의 LLM 답변을 놓고 (별개의) LLM Judge가 어느 답변이 더 좋은지 투표하는 방식
      1. Single answer grading: 하나의 LLM 답변을 놓고 LLM Judge가 점수를 매기는 방식 (1-10 scale 등)
      1. Reference-guided grading: 특정한 경우에, 정답을 알고 있는 상황에서 답변을 평가하는 방식
  • 또한 LLM-as-a-Judge의 한계점도 제시하며 몇 가지 해결책을 제시한다.
      1. Position bias: 첫 번째 답변이 더 좋은 점수를 받는 경향이 있다.
        • Swapping positions: 이는 답변 두 개의 순서를 바꿔서 제시한 뒤 특정 답변이 둘 다 좋은 점수를 받을 때만 우수한 답변으로 평가하는 방식으로 해결할 수 있다.
      1. Verbosity bias: 더 긴 답변이 더 좋은 점수를 받는 경향이 있다.
        • Few-shot judge: Few-shot 방식으로 해결할 수 있다. 하지만 비용이 비싸져서 이 논문에서는 Zero-shot 방식 평가한다.
      1. Self-enhancement bias: 자기 자신의 답변이 더 좋은 점수를 받는 경향이 있다.
        • Chain-of-thought and reference-guided judge: CoT 방식은 일부 문제를 해결할 수 있지만 LLM Judge에게 따로 물어봤을 때는 정답을 맞추는 문제도 답변을 평가하라 하면 잘못된 평가를 내리기도 한다. 그래서 먼저 LLM Judge에게 정답을 물어보고, 이를 reference 삼아 다시 평가하도록 하는 방법을 사용한다.
      1. Fine-tuning a judge model: Vicuna-13B 모델을 arena data에 훈련시켜서 모델을 개선한다.

MT-Bench는 다음처럼 multi-turn으로 이루어진 질문 세트로 LLM의 multi-turn 대화 성능과 instruction-following 능력을 평가한다.

Chatbot Arena는 다음처럼 두 개의 LLM 답변을 놓고 사용자는 어느 답변이 더 좋은지 투표하는 방식으로 LLM의 성능을 평가한다.

답변 평가는 아래 그림과 같이 진행한다.

참고:

  • Metrics: We define the agreement between two types of judges as the probability of randomly selected individuals (but not identical) of each type agreeing on a randomly selected question. See more explanation in Appendix D.3. Average win rate is the average of win rates against all other players. These metrics can be computed with or without including tie votes.
Comment  Read more

Adapting-large-language-models-to-domains-via-reading-comprehension 요약 설명

|

이번 글에서는 AdaptLLM: Adapting-large-language-models-to-domains-via-reading-comprehension 논문의 핵심 포인트만 간단히 정리한다.

  • 2023년 7월(Arxiv), ICLR 2024
  • Daixuan Cheng, Shaohan Huang B & Furu Wei.
  • Microsoft Research,Beijing Institute for General Artificial Intelligence (BIGAI)
  • 논문 링크
  • Github
  • Huggingface

요약

  • Domain-specific corpora에서 pre-training 하는 것은 모델이 해당 domain에 대한 지식을 학습할 수 있도록 해 주지만, 반대로 해당 domain 외 일반적 task, 또는 prompting 능력을 저하시킬 수 있다.
  • 이 논문에서는
    • 해당 사실을 발견하여, 이를 해결할 수 있는 방법으로
    • raw corpora를 reading comprehension text로 변환시켜 해당 데이터로 학습하는 과정을 통해 prompting 성능을 떨어뜨리지 않으면서도 domain-specific 지식을 학습하는 방법을 제안한다.
  • 결과적으로
    • biomedicine, finance, law 3가지 분야에서 보다 큰 모델과 비슷한 수준의 성능을 확보하였고 (특히, finance에서는 BloombergGPT와 비슷한 수준의 성능을 보였다)
    • 그러면서도 일반적 성능이 떨어지지 않음을 실험을 통해 보였다.
  • 논문의 핵심 내용은 위의 요약과 같다.
  • 남은 중요한 부분은 raw corpora를 reading comprehension text로 변환시키는 방법에 대한 부분인데,
    • 아래 figure 02처럼 $-$ 그냥 평범한 글을, 특정한 regex expression에 맞는 부분이 있으면 그것을 일종의 QA task처럼 바꾸는 과정이다.
    • 이렇게 생성된 task들을 해당 domain에 맞는 데이터로 학습시키면 해당 domain에 대한 지식을 학습할 수 있게 된다.
    • 생성한 task는 보통의 QA task와 비슷하므로, 그냥 raw corpora를 학습시키는 것에 비해 모델이 원래 가지고 있던 instruction-following 능력을 저하시키지 않는다.
    • 발굴한 task들은 요약, 특정 주제에 대한 문장 생성, 추론, 문단 완성 등이 있다.
  • 전체 pattern은 아래 table 02와 같다.
  • 코멘트. 간단한 방식으로 raw corpora를 reading comprehension task로 변환시키는 방법이 매우 흥미롭다.
Comment  Read more

seaborn 사용법(python seaborn 사용법)

|

이 글에서는 python seaborn의 사용법을 정리한다.

따로 명시하지 않으면 이 글에서의 예제 데이터는 다음으로 설정한다.

data = pd.DataFrame(data={
    'A': [1,4,3,6,5,8,7,9],
    'B': [6,5,7,8,9,9,8,9],
    'C': [8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1]
})

seaborn 설치

설치는 꽤 간단하다.

pip install seaborn

import는 관례적으로 다음과 같이 한다.

import seaborn as sns

막대 그래프(barplot)

sns.barplot(data=data)

그래프 배경 설정(set)

  • 기본값은 style="darkgrid"이고 darkgrid, whitegrid, dark, white, ticks 테마가 있다.
  • sns.set(style='darkgrid') 또는 sns.set_style('whitegrid')와 같이 사용한다.
sns.set() 
plt.scatter(x=data.index, y=data['A'])
sns.set_style('dark')
plt.scatter(x=data.index, y=data['A'])

Heatmap

sns.heatmap(data)

눈금 값 설정(xticks, yticks)

heatmap을 구성하는 각 box의 중심 좌표는 제일 왼쪽 아래가 (0.5, 0.5)이다. 즉 0.5만큼을 더해줘야 한다. 여기서 벗어나게 지정할 수도 있지만 이상해 보인다.

sns.heatmap(data)
plt.xticks([0.5, 1.5, 2.9], ["A class", "B class", "C class"])

데이터 값 표시(annot, fmt)

  • 데이터 값을 표시하려면 annot=True를 지정한다.
  • fmt 인자는 d, .2f와 같이 지정할 수 있다.
sns.heatmap(data, annot = True, fmt = ".2f")

선 스타일 설정

다른 그래프와 비슷하게 linewidth, linecolor로 설정할 수 있다.

sns.heatmap(data, annot = True, fmt = ".1f", linewidth = 2, linecolor = "black")

Colormap 설정

sns.heatmap(data, annot = True, fmt = ".2f", cmap = "Blues", linewidth = 1, linecolor = "black")

cmap은 아래 그림들을 참조하자.

위 그림은 아래 코드로 생성할 수 있다.

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

cmaps = {}

gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))


def plot_color_gradients(category, cmap_list):
    # Create figure and adjust figure height to number of colormaps
    nrows = len(cmap_list)
    figh = 0.35 + 0.15 + (nrows + (nrows - 1) * 0.1) * 0.22
    fig, axs = plt.subplots(nrows=nrows + 1, figsize=(6.4, figh))
    fig.subplots_adjust(top=1 - 0.35 / figh, bottom=0.15 / figh,
                        left=0.2, right=0.99)
    axs[0].set_title(f'{category} colormaps', fontsize=14)

    for ax, name in zip(axs, cmap_list):
        ax.imshow(gradient, aspect='auto', cmap=mpl.cm.get_cmap(name))
        ax.text(-0.01, 0.5, name, va='center', ha='right', fontsize=10,
                transform=ax.transAxes)

    # Turn off *all* ticks & spines, not just the ones with colormaps.
    for ax in axs:
        ax.set_axis_off()

    # Save colormap list for later.
    cmaps[category] = cmap_list


References

  • https://matplotlib.org/3.7.1/tutorials/colors/colormaps.html
Comment  Read more

matplotlib 사용법(python matplotlib.pyplot 사용법)

|

이 글에서는 python matplotlib의 사용법을 정리한다.

따로 명시하지 않으면 이 글에서의 예제 데이터는 다음으로 설정한다.

data = pd.DataFrame(data={
    'A': [1,4,3,6,5,8,7,9],
    'B': [6,5,7,8,9,9,8,9],
    'C': [8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1]
})

matplotlib 설치

설치는 꽤 간단하다.

pip install matplotlib

import는 관례적으로 다음과 같이 한다.

from matplotlib import pyplot as plt
import numpy as np
import pandas as pd

Font 설정

아무 설정을 하지 않으면 한글이 깨지는 경우가 많다.

RuntimeWarning: Glyph 44256 missing from current font.
  font.set_text(s, 0.0, flags=flags)

보통 다음과 같이 설정해주면 된다.

from matplotlib import rcParams
rcParams["font.family"] = "Malgun Gothic"
rcParams["axes.unicode_minus"] = False

참고로 설정 가능한 폰트 목록을 확인하고 싶다면 다음 코드를 실행해 보자.

import matplotlib.font_manager
fpaths = matplotlib.font_manager.findSystemFonts()
font_names = []
for i in fpaths:
    f = matplotlib.font_manager.get_font(i)
    font_names.append(f.family_name)

print(font_names[:5])

for fn in font_names:
    if 'malgun' in fn.lower():
        print(fn)

Jupyter 전용 기능

다음 magic command를 사용사면 plt.show() 함수를 사용하지 않아도 그래프를 보여준다.

%matplotlib inline
plt.scatter(x=[1,2,3], y=[4,5,6])

보통의 환경이라면 plt.show() 함수를 사용해야 그래프가 보인다. PyCharm이라면 SciView 창에서 열린다.


그래프 종류

산점도(scatter)

plt.scatter(x=[1,2,3], y=[4,5,6])

선 그래프(plot)

plt.plot(data.index, data['B'])
# 하나만 입력하면 기본 index로 그려진다.
plt.plot(data['C']) 
# 한 번의 plot() 호출로 여러 개를 그릴 수 있다. 순서는 x, y, fmt 순으로 입력할 수 있다.
t = np.arange(0., 5., 0.2)
plt.plot(t, t, 'r--', t, t**2, 'bs-', t, t**3, 'g^-.')

plot()은 list나 DataFrame뿐 아니라 dictionary도 그래프로 나타낼 수 있다.

data_dict = {'x': [1, 2, 3, 4, 5], 'y': [2, 3, 5, 7, 11]}
plt.plot('x', 'y', data=data_dict)

막대 그래프(bar)

plt.bar(data.index, data['B'])

2개 그룹 동시 표시

stack해서 사용하는 방법은 다음과 같이 bottom 옵션을 사용한다.

p1 = plt.bar(data.index, data['B'], color='red', alpha=0.7)
p2 = plt.bar(data.index, data['C'], color='blue', alpha=0.7, bottom=data['B'])
# plot이 여러 개인 경우 plt.legend()는 그냥 넣으면 legend가 출력되지 않는다.

나란히 놓는 방법은 bar의 width 옵션과 x좌표를 조정하면 된다.

p1 = plt.bar(data.index-0.2, data['B'], color='red', alpha=0.7, width=0.4)
p2 = plt.bar(data.index+0.2, data['C'], color='blue', alpha=0.7, width=0.4)
plt.legend((p1, p2), ('B', 'C'), fontsize=12)

boxplot

x = np.random.normal(50, 5, 100)
plt.boxplot(x)

여러 boxplot을 한 번에 그리려면 리스트에 담아서 전달한다.

x1 = np.random.normal(15, 5, 500)
x2 = np.random.normal(10, 10, 100)
plt.boxplot([x1, x2])
plt.xticks([1, 2], ["x1", "x2"])

Histogram

구간 개수는 bins으로 설정한다.

x = np.random.normal(10, 2, 100)
plt.hist(x, bins=10)

Heatmap

matshow라는 함수가 있지만 이건 seaborn의 heatmap이 더 편하다.

pd.DataFrame.plot

  • pandas의 dataframe에서 .plot() method를 호출하면 바로 그래프를 그릴 수 있다.
    • 종류는 line, bar, barh, hist, box, scatter 등이 있다. barh는 수평 막대 그래프를 의미한다.
data.plot(kind='line')

좀 더 자세하게 설정할 수도 있다. 이 글에 있는 스타일 설정을 대부분 적용할 수 있다.

data.plot(kind = "bar", y = ["B", "C"], figsize = (10, 6),
    yticks=[0, 5, 10])

단 boxplot과 histogram은 y만 설정할 수 있다.


스타일 설정

더 많은 설정은 여기를 참고하자.

  • dashes 옵션으로 직접 dash line을 조작할 수 있다.
  • markevery 옵션으로 마커를 만들 샘플을 추출할 수 있다. 옵션값이 5(int)면 5개의 샘플마다, float이면 상대적 거리에 따라 추출한다.
  • visible 옵션으로 선을 안 보이게 할 수 있다.
  • fillstyle 옵션으로 마커를 채우는 방식을 설정할 수 있다. full, left, right, bottom, top, none 가능

format string(fmt)

색상, 마커, 선 스타일을 쉽게 지정할 수 있다.

plt.plot(data['A'], 'b.-', label='A')
plt.plot(data['B'], 'cv--', label='B') 
plt.plot(data['C'], 'm|:', label='C')
plt.legend()

색상(color)

plt.plot(data['B'], label='B', color='red')
plt.plot(data['C'], label='C', color='green')
plt.legend()
fmt color
b blue
g green
r red
c cyan
m magenta
y yellow
k black
w white

선 스타일(linestyle), 두께(linewidth)

  • 선 스타일은 linestyle parameter를 전달하며 기본값인 soliddashed, dotted, dashdot이 있다.
  • 선 두께는 linewidth로 설정하고 기본값은 1.5이다.
plt.plot(data['A'], label='A', linestyle='dashed', linewidth=2)
plt.plot(data['B'], label='B', linestyle='dotted', linewidth=3)
plt.plot(data['C'], label='C', linestyle='dashdot', linewidth=4)
plt.legend()
fmt linestyle
- solid
-- dashed
: dotted
-. dashdot

수치로 직접 지정할 수 있다. 참고로 (0, (1, 1))dotted, (0, (5, 5))dashed, (0, (3, 5, 1, 5))dashdot과 같다.

plt.plot(data['A'], label='A', linestyle=(0, (1,1)), linewidth=2)
plt.plot(data['B'], label='B', linestyle=(0, (4,1)), linewidth=3)
plt.plot(data['C'], label='C', linestyle=(0, (1,4)), linewidth=4)
plt.legend()

bar 스타일(width, color, linewidth)

bar의 두께를 설정할 수 있다. 각 데이터마다 다른 값을 주면 각각 다르게 설정할 수도 있다.

plt.bar(data.index, data['A'], width=0.4, color=["red", "blue", "green", "purple", "red", "blue", "green", "purple"], linewidth=2.5)

마커(marker)

각 data point마다 marker를 표시할 수 있다.

  • 점: ., 원: o, 사각형: s, 별: *, 다이아몬드: D, d
  • marker의 사이즈도 markersize parameter로 지정할 수 있다.
  • 산점도(scatter)의 마커 크기는 s parameter로 설정한다. 단 크기가 10배 차이난다.
plt.plot(data['A'], label='A', marker='o', markersize=4)
plt.plot(data['B'], label='B', marker='s', markersize=8)
plt.scatter(data.index, data['C'], label='C', marker='.', s=120, color='red')
plt.legend()
fmt marker 설명 fmt marker 설명
. point s square 사각형
, pixel 픽셀 p pentagon 오각형
o circle * star
v triangle_down 역삼각형 h hexagon1 육각형 1
^ triangle_up 삼각형 H hexagon2 육각형 2
< triangle_left 삼각형(왼쪽) + plus + 모양
> triangle_right 삼각형(오른쪽) x x x 모양
1 tri_down 삼각뿔(아래쪽) D diamond 다이아몬드
2 tri_up 삼각뿔(위쪽) d thin diamond 얇은 다이아몬드
3 tri_left 삼각뿔(왼쪽) | vline v line
4 tri_right 삼각뿔(위쪽) _ hline h line

더 많은 마커 옵션이 있다.

투명도(alpha)

데이터가 너무 많은 경우 투명도를 조절하면 좀 더 잘 보이게 할 수 있다.

import numpy as np
x = np.random.normal(0, 1, 16384)
y = np.random.normal(0, 1, 16384)
plt.scatter(x, y, alpha = 0.04, color='purple')

그래프 전체 설정

그래프 크기 설정

plt.figure(figsize=(6, 3))
plt.scatter(x=data.index, y=data['A'])

그래프 제목(title)

plt.scatter(x=data.index, y=data['A'])
plt.title("Gorio")

범례 설정(legend)

기본적으로 plot(label=...) 등으로 label을 등록하면, plt.legend()로 등록된 label들을 표시해주는 개념이다.

plt.scatter(x=data.index, y=data['A'], label='Gorio')
plt.legend()

legend 위치는 그래프를 가리지 않는 위치에 임의로 생성된다. 위치를 지정하려면 loc parameter를 설정한다.

plt.scatter(x=data.index, y=data['A'], label='Gorio')
plt.legend(loc='lower right')

가능한 옵션을 총 9개이다. left, center, right, upper left, upper center, upper right, lower left, lower center, lower right

참고로 loc=(0.5, 0.5)와 같이 직접 수치를 지정할 수도 있다. loc=(0.0, 0.0)은 왼쪽 아래, loc=(1.0, 1.0)은 오른쪽 위이다.

legend() 메서드에서도 label을 직접 등록하여 표시할 수 있다.

p1 = plt.bar(data.index-0.2, data['A'], color='red', alpha=0.7, width=0.4)
p2 = plt.bar(data.index+0.2, data['C'], color='blue', alpha=0.7, width=0.4)
plt.legend((p1, p2), ('A', 'C'), fontsize=12)

ncol 옵션으로 범례에 표시되는 텍스트의 열 개수를 지정할 수 있다.

p1 = plt.bar(data.index-0.2, data['A'], color='red', alpha=0.7, width=0.4, label='A')
p2 = plt.bar(data.index+0.2, data['C'], color='blue', alpha=0.7, width=0.4, label='C')
plt.legend(ncol=2)

각종 다양한 스타일을 지정할 수 있다.

p1 = plt.bar(data.index-0.2, data['A'], color='red', alpha=0.7, width=0.4, label='A')
p2 = plt.bar(data.index+0.2, data['C'], color='blue', alpha=0.7, width=0.4, label='C')
plt.legend(frameon=True, shadow=True, facecolor='inherit', edgecolor='green', borderpad=0.8, labelspacing=1.1)

더 자세한 설정은 여기에서 확인 가능하다.


x, y축 설정

축 제목(xlabel, ylabel)

plt.scatter(x=data.index, y=data['A'])
plt.xlabel("x axis")
plt.ylabel("y axis")

축 제목과 축 간 거리는 labelpad로 조정한다.

plt.plot(data.index, data['A'])
plt.xlabel("x axis", labelpad=20)
plt.ylabel("y axis", labelpad=-1)

폰트 설정도 할 수 있다.

font1 = {'family': 'serif',
         'color': 'b',
         'weight': 'bold',
         'size': 14
         }

font2 = {'family': 'fantasy',
         'color': 'deeppink',
         'weight': 'normal',
         'size': 'xx-large'
         }

plt.plot([1, 2, 3, 4], [2, 3, 5, 7])
plt.xlabel('x Axis', labelpad=15, fontdict=font1)
plt.ylabel('y Axis', labelpad=20, fontdict=font2)

축 범위 설정(xlim, ylim, axis)

각 함수를 호출하면 return value로 x축, y축, x 및 y축의 최솟값/최댓값을 얻을 수 있다.

plt.scatter(x=data.index, y=data['A'])
xmin, xmax = plt.xlim(left=0, right=10)
ymin, ymax = plt.ylim(bottom=0, top=10)
# 아래 한 줄로도 쓸 수 있다.
# xmin, xmax, ymin, ymax = plt.axis([0,10,0,10])

참고로 left, right, bottom, top 중 설정하지 않은 값은 데이터 최솟값/최댓값에 맞춰 자동으로 설정된다.

범위를 수치로 직접 설정하는 대신 그래프의 비율이나 scale을 조정할 수 있다.
scaled의 경우 다음과 같이 x축 간격과 y축 간격의 scale이 같아진다.

plt.scatter(x=data.index, y=data['A'])
xmin, xmax, ymin, ymax = plt.axis('scaled')
# (-0.35000000000000003, 8.450000000000001, 0.6, 9.4)

다음과 같은 옵션들이 있다: 'on' | 'off' | 'equal' | 'scaled' | 'tight' | 'auto' | 'normal' | 'image' | 'square'

눈금 값 설정(xticks, yticks)

ticks에 tick의 위치를 지정하고, labels에 원하는 tick 이름을 지정할 수 있다.

plt.scatter(x=data.index, y=data['A'])
plt.xticks(ticks=[0,3,6], labels=['zero', 'three', 'six'])
plt.ylim(bottom=0, top=10)

xticks나 yticks에 값을 1개만 주면 ticks parameter가 설정된다.

plt.scatter(x=data.index, y=data['A'])
plt.xticks([0,3,6])
plt.ylim(bottom=0, top=10)

참고로 막대그래프는 기본적으로 막대의 중심 좌표를 기준으로 계산하지만 align parameter를 주면 왼쪽 위치로 계산할 수 있다.

plt.bar(data.index, data['B'], align='edge')
plt.xticks([0,3,6], ['zero', 'three', 'six'])

그래프 저장(savefig)

plt.scatter(x=data.index, y=data['A'])
plt.savefig("gorio.png", dpi=300)

dpi는 dot per inch의 약자이다. 높을수록 해상도가 좋아지고 파일 크기도 커진다.


References

  • https://wikidocs.net/book/5011
  • https://matplotlib.org/stable/api/_as_gen/matplotlib.lines.Line2D.html
Comment  Read more