Skip to content

Commit 56e7bdf

Browse files
committed
Update CI TF matrix and add example smoke coverage
1 parent c8efad0 commit 56e7bdf

20 files changed

Lines changed: 712 additions & 459 deletions

.github/workflows/ci.yml

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,84 @@
11
name: CI
22

3-
on:
3+
on:
44
push:
5-
path:
6-
- 'ge/*'
7-
- 'tests/*'
5+
paths:
6+
- "ge/**"
7+
- "tests/**"
8+
- "examples/**"
9+
- ".github/workflows/**"
10+
- "setup.py"
11+
- "README.md"
812
pull_request:
9-
path:
10-
- 'ge/*'
11-
- 'tests/*'
12-
13+
paths:
14+
- "ge/**"
15+
- "tests/**"
16+
- "examples/**"
17+
- ".github/workflows/**"
18+
- "setup.py"
19+
- "README.md"
20+
1321
jobs:
1422
build:
15-
16-
runs-on: ubuntu-latest
23+
runs-on: ubuntu-22.04
1724
timeout-minutes: 180
1825
strategy:
26+
fail-fast: false
1927
matrix:
20-
python-version: [3.6,3.7,3.8]
21-
tf-version: [1.4.0,1.15.0,2.5.0,2.6.0,2.7.0,2.8.0,2.9.0]
22-
23-
exclude:
24-
- python-version: 3.7
25-
tf-version: 1.4.0
26-
- python-version: 3.7
27-
tf-version: 1.15.0
28-
- python-version: 3.8
29-
tf-version: 1.4.0
30-
- python-version: 3.8
31-
tf-version: 1.14.0
32-
- python-version: 3.8
33-
tf-version: 1.15.0
34-
- python-version: 3.6
35-
tf-version: 2.7.0
36-
- python-version: 3.6
37-
tf-version: 2.8.0
38-
- python-version: 3.6
39-
tf-version: 2.9.0
40-
- python-version: 3.9
41-
tf-version: 1.4.0
42-
- python-version: 3.9
43-
tf-version: 1.15.0
44-
- python-version: 3.9
45-
tf-version: 2.2.0
28+
include:
29+
- python-version: "3.7"
30+
tf-version: "1.15.5"
31+
use-legacy-keras: "0"
32+
- python-version: "3.10"
33+
tf-version: "2.10.0"
34+
use-legacy-keras: "0"
35+
- python-version: "3.10"
36+
tf-version: "2.15.0"
37+
use-legacy-keras: "0"
38+
- python-version: "3.11"
39+
tf-version: "2.15.0"
40+
use-legacy-keras: "0"
41+
- python-version: "3.10"
42+
tf-version: "2.20.0"
43+
use-legacy-keras: "1"
44+
- python-version: "3.11"
45+
tf-version: "2.20.0"
46+
use-legacy-keras: "1"
47+
- python-version: "3.12"
48+
tf-version: "2.20.0"
49+
use-legacy-keras: "0"
50+
4651
steps:
47-
48-
- uses: actions/checkout@v3
49-
50-
- name: Setup python environment
51-
uses: actions/setup-python@v4
52-
with:
53-
python-version: ${{ matrix.python-version }}
52+
- uses: actions/checkout@v4
53+
54+
- name: Setup Python environment
55+
uses: actions/setup-python@v5
56+
with:
57+
python-version: ${{ matrix.python-version }}
58+
59+
- name: Install dependencies
60+
run: |
61+
python -m pip install --upgrade pip
62+
python -m pip install -q "tensorflow==${{ matrix.tf-version }}"
63+
if [[ "${{ matrix.tf-version }}" == 1.* ]]; then
64+
python -m pip install -q "protobuf==3.20.3"
65+
fi
66+
if [[ "${{ matrix.use-legacy-keras }}" == "1" ]]; then
67+
python -m pip install -q "tf-keras~=2.20"
68+
fi
69+
python -m pip install -e ".[test]"
70+
71+
- name: Test with pytest
72+
timeout-minutes: 180
73+
env:
74+
TF_USE_LEGACY_KERAS: ${{ matrix.use-legacy-keras }}
75+
run: |
76+
pytest --cov=ge --cov=examples --cov-report=xml
5477
55-
- name: Install dependencies
56-
run: |
57-
pip3 install -q tensorflow==${{ matrix.tf-version }}
58-
pip install -q protobuf==3.19.0
59-
pip install -q requests
60-
pip install -e .
61-
- name: Test with pytest
62-
timeout-minutes: 180
63-
run: |
64-
pip install -q pytest
65-
pip install -q pytest-cov
66-
pip install -q python-coveralls
67-
pytest --cov=ge --cov-report=xml
68-
- name: Upload coverage to Codecov
69-
uses: codecov/codecov-action@v3.1.0
70-
with:
71-
token: ${{secrets.CODECOV_TOKEN}}
72-
file: ./coverage.xml
73-
flags: pytest
74-
name: py${{ matrix.python-version }}-tf${{ matrix.tf-version }}
78+
- name: Upload coverage to Codecov
79+
uses: codecov/codecov-action@v4
80+
with:
81+
token: ${{ secrets.CODECOV_TOKEN }}
82+
file: ./coverage.xml
83+
flags: pytest
84+
name: py${{ matrix.python-version }}-tf${{ matrix.tf-version }}

README.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,34 @@
2121
| Struc2Vec | [KDD 2017][struc2vec: Learning Node Representations from Structural Identity](https://arxiv.org/pdf/1704.03165.pdf) | [【Graph Embedding】Struc2Vec:算法原理,实现和应用](https://zhuanlan.zhihu.com/p/56733145) |
2222

2323

24+
# CI Compatibility Matrix
25+
26+
The CI matrix currently validates the following Python/TensorFlow combinations:
27+
28+
| Python | TensorFlow | `TF_USE_LEGACY_KERAS` |
29+
| :----: | :--------: | :-------------------: |
30+
| 3.7 | 1.15.5 | 0 |
31+
| 3.10 | 2.10.0 | 0 |
32+
| 3.10 | 2.15.0 | 0 |
33+
| 3.11 | 2.15.0 | 0 |
34+
| 3.10 | 2.20.0 | 1 |
35+
| 3.11 | 2.20.0 | 1 |
36+
| 3.12 | 2.20.0 | 0 |
37+
38+
For TensorFlow 2.16+ jobs that need legacy Keras behavior, CI installs `tf-keras` and sets `TF_USE_LEGACY_KERAS=1`.
39+
2440
# How to run examples
25-
1. clone the repo and make sure you have installed `tensorflow` or `tensorflow-gpu` on your local machine.
26-
2. run following commands
41+
42+
1. Clone the repo and install dependencies.
43+
2. Run one example script.
44+
2745
```bash
28-
python setup.py install
29-
cd examples
30-
python deepwalk_wiki.py
46+
pip install -e .[cpu]
47+
python examples/deepwalk_wiki.py
3148
```
3249

50+
Each example now exposes `main(smoke=False, show=True)`. CI executes all `examples/*.py` in smoke mode (`smoke=True`, `show=False`) to keep runtime short while still validating the training/import paths.
51+
3352
## DisscussionGroup & Related Projects
3453

3554
<html>

examples/alias.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,37 @@
44
from ge.alias import alias_sample, create_alias_table
55

66

7-
def gen_prob_dist(N):
8-
p = np.random.randint(0, 100, N)
9-
return p/np.sum(p)
7+
def gen_prob_dist(size):
8+
probabilities = np.random.randint(0, 100, size)
9+
return probabilities / np.sum(probabilities)
1010

1111

12-
def simulate(N=100, k=10000,):
12+
def simulate(size=100, sample_count=10000):
13+
truth = gen_prob_dist(size)
14+
accept, alias = create_alias_table(truth)
1315

14-
truth = gen_prob_dist(N)
16+
sampled = np.zeros(size)
17+
for _ in range(sample_count):
18+
sampled[alias_sample(accept, alias)] += 1
19+
return sampled / np.sum(sampled), truth
1520

16-
area_ratio = truth
17-
accept, alias = create_alias_table(area_ratio)
1821

19-
ans = np.zeros(N)
20-
for _ in range(k):
21-
i = alias_sample(accept, alias)
22-
ans[i] += 1
23-
return ans/np.sum(ans), truth
22+
def main(smoke=False, show=True):
23+
size = 20 if smoke else 100
24+
sample_count = 300 if smoke else 10000
25+
alias_result, truth = simulate(size=size, sample_count=sample_count)
26+
27+
assert np.isclose(alias_result.sum(), 1.0)
28+
assert np.isclose(truth.sum(), 1.0)
29+
30+
if show:
31+
plt.bar(list(range(len(alias_result))), alias_result, label="alias_result")
32+
plt.bar(list(range(len(truth))), truth, label="truth")
33+
plt.legend()
34+
plt.show()
35+
36+
return alias_result, truth
2437

2538

2639
if __name__ == "__main__":
27-
alias_result, truth = simulate()
28-
plt.bar(list(range(len(alias_result))), alias_result, label='alias_result')
29-
plt.bar(list(range(len(truth))), truth, label='truth')
30-
plt.legend()
40+
main()

examples/deepwalk_wiki.py

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,73 @@
1+
from pathlib import Path
12

3+
import matplotlib.pyplot as plt
4+
import networkx as nx
25
import numpy as np
6+
from sklearn.linear_model import LogisticRegression
7+
from sklearn.manifold import TSNE
38

4-
from ge.classify import read_node_label, Classifier
59
from ge import DeepWalk
6-
from sklearn.linear_model import LogisticRegression
10+
from ge.classify import Classifier, read_node_label
711

8-
import matplotlib.pyplot as plt
9-
import networkx as nx
10-
from sklearn.manifold import TSNE
12+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
13+
WIKI_GRAPH_PATH = PROJECT_ROOT / "data" / "wiki" / "Wiki_edgelist.txt"
14+
WIKI_LABEL_PATH = PROJECT_ROOT / "data" / "wiki" / "wiki_labels.txt"
15+
SMOKE_GRAPH_PATH = PROJECT_ROOT / "tests" / "Wiki_edgelist.txt"
1116

1217

13-
def evaluate_embeddings(embeddings):
14-
X, Y = read_node_label('../data/wiki/wiki_labels.txt')
15-
tr_frac = 0.8
16-
print("Training classifier using {:.2f}% nodes...".format(
17-
tr_frac * 100))
18+
def evaluate_embeddings(embeddings, label_path):
19+
x_data, y_data = read_node_label(str(label_path))
20+
train_fraction = 0.8
21+
print("Training classifier using {:.2f}% nodes...".format(train_fraction * 100))
1822
clf = Classifier(embeddings=embeddings, clf=LogisticRegression())
19-
clf.split_train_evaluate(X, Y, tr_frac)
20-
23+
clf.split_train_evaluate(x_data, y_data, train_fraction)
2124

22-
def plot_embeddings(embeddings,):
23-
X, Y = read_node_label('../data/wiki/wiki_labels.txt')
2425

25-
emb_list = []
26-
for k in X:
27-
emb_list.append(embeddings[k])
28-
emb_list = np.array(emb_list)
26+
def plot_embeddings(embeddings, label_path, show=True):
27+
x_data, y_data = read_node_label(str(label_path))
2928

30-
model = TSNE(n_components=2)
31-
node_pos = model.fit_transform(emb_list)
29+
embedding_list = np.array([embeddings[node] for node in x_data])
30+
node_pos = TSNE(n_components=2).fit_transform(embedding_list)
3231

3332
color_idx = {}
34-
for i in range(len(X)):
35-
color_idx.setdefault(Y[i][0], [])
36-
color_idx[Y[i][0]].append(i)
33+
for index, label in enumerate(y_data):
34+
color_idx.setdefault(label[0], [])
35+
color_idx[label[0]].append(index)
3736

38-
for c, idx in color_idx.items():
39-
plt.scatter(node_pos[idx, 0], node_pos[idx, 1], label=c)
37+
for label, indexes in color_idx.items():
38+
plt.scatter(node_pos[indexes, 0], node_pos[indexes, 1], label=label)
4039
plt.legend()
41-
plt.show()
40+
if show:
41+
plt.show()
42+
else:
43+
plt.close()
4244

4345

44-
if __name__ == "__main__":
45-
G = nx.read_edgelist('../data/wiki/Wiki_edgelist.txt',
46-
create_using=nx.DiGraph(), nodetype=None, data=[('weight', int)])
46+
def main(smoke=False, show=True):
47+
graph_path = SMOKE_GRAPH_PATH if smoke else WIKI_GRAPH_PATH
48+
graph = nx.read_edgelist(
49+
str(graph_path),
50+
create_using=nx.DiGraph(),
51+
nodetype=None,
52+
data=[("weight", int)],
53+
)
4754

48-
model = DeepWalk(G, walk_length=10, num_walks=80, workers=1)
49-
model.train(window_size=5, iter=3)
55+
model = DeepWalk(
56+
graph,
57+
walk_length=3 if smoke else 10,
58+
num_walks=2 if smoke else 80,
59+
workers=1,
60+
)
61+
model.train(window_size=2 if smoke else 5, iter=1 if smoke else 3, workers=1)
5062
embeddings = model.get_embeddings()
63+
assert len(embeddings) > 0
64+
65+
if not smoke:
66+
evaluate_embeddings(embeddings, WIKI_LABEL_PATH)
67+
plot_embeddings(embeddings, WIKI_LABEL_PATH, show=show)
5168

52-
evaluate_embeddings(embeddings)
53-
plot_embeddings(embeddings)
69+
return embeddings
70+
71+
72+
if __name__ == "__main__":
73+
main()

0 commit comments

Comments
 (0)